commit 32485e2860c2ea31fcef5b575f446c7a3036a550 Author: ThΓ©ophile Diot Date: Fri Jun 30 15:38:03 2023 -0400 Squashed 'src/deps/src/lua-gd/' content from commit 2ce8e478a git-subtree-dir: src/deps/src/lua-gd git-subtree-split: 2ce8e478a8591afd71e607506bc8c64b161bbd30 diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..bde358438 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.so +*.lo +*.tar.gz + +demos/out.png +demos/out.gif +demos/counter.txt + diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..11befb5bc --- /dev/null +++ b/COPYING @@ -0,0 +1,23 @@ +Lua-GD (c) 2004-2006 Alexandre Erwin Ittner + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +If you use this package in a product, an acknowledgment in the product +documentation would be greatly appreciated (but it is not required). diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..21f1d6fd5 --- /dev/null +++ b/Makefile @@ -0,0 +1,124 @@ +# luagd -- gd bindings for the Lua Programming Language. +# (c) 2004-11 Alexandre Erwin Ittner +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# If you use this package in a product, an acknowledgment in the product +# documentation would be greatly appreciated (but it is not required). +# + + +# Lua-GD version. This one must be set. +VERSION=2.0.33r3 + +# Command used to run Lua code +LUABIN=lua5.1 + +# Optimization for the brave of heart ;) +OMITFP=-fomit-frame-pointer + + +# --------------------------------------------------------------------------- +# Automatic configuration using pkgconfig. These lines should work on most +# Linux/Unix systems. If your system does not have these programs you must +# comment out these lines and uncomment and change the next ones. + +# Name of .pc file. "lua5.1" on Debian/Ubuntu +LUAPKG=lua5.1 +OUTFILE=gd.so + +CFLAGS=-O3 -Wall -fPIC $(OMITFP) +CFLAGS+=`pkg-config $(LUAPKG) --cflags` +CFLAGS+=-DVERSION=\"$(VERSION)\" + +GDFEATURES=-DGD_XPM -DGD_JPEG -DGD_FONTCONFIG -DGD_FREETYPE -DGD_PNG -DGD_GIF +LFLAGS=-shared `pkg-config $(LUAPKG) --libs` -lgd + +INSTALL_PATH := `pkg-config $(LUAPKG) --variable=INSTALL_CMOD` + + +# --------------------------------------------------------------------------- +# Manual configuration for systems without pkgconfig. +# WARNING: These instructions will only work on older versions of GD, since +# gdlib-config has been removed in favor of pkg-config. + +# Path to the utility 'gdlib-config'. This may be changed to compile the +# module with development versions of libgd. +#GDLIBCONFIG=gdlib-config + +#OUTFILE=gd.so +#CFLAGS=-O3 -Wall -fPIC $(OMITFP) +#CFLAGS+=`$(GDLIBCONFIG) --cflags` -I/usr/include/lua5.1 +#CFLAGS+=-DVERSION=\"$(VERSION)\" +#GDFEATURES=`$(GDLIBCONFIG) --features |sed -e "s/GD_/-DGD_/g"` +#LFLAGS=-shared `$(GDLIBCONFIG) --ldflags` `$(GDLIBCONFIG) --libs` -lgd +#INSTALL_PATH=/usr/lib/lua/ + + +# --------------------------------------------------------------------------- +# Manual configuration for Windows and systems without sed, pkgconfig, etc. +# Uncomment, change and good luck :) + +#OUTFILE=gd.dll +#CFLAGS=-O3 -Wall -fPIC $(OMITFP) +#CFLAGS+=-IC:/lua5.1/ +#CFLAGS+=-DVERSION=\"$(VERSION)\" +#GDFEATURES=-DGD_XPM -DGD_JPEG -DGD_FONTCONFIG -DGD_FREETYPE -DGD_PNG -DGD_GIF +#LFLAGS=-shared -lgd2 -lm $(OMITFP) +#INSTALL_PATH="C:/Program Files/lua/" +# --------------------------------------------------------------------------- + + +all: test + +$(OUTFILE): gd.lo + $(CC) -o $(OUTFILE) gd.lo $(LFLAGS) + +test: $(OUTFILE) + $(LUABIN) test_features.lua + +gd.lo: luagd.c + $(CC) -o gd.lo -c $(GDFEATURES) $(CFLAGS) luagd.c + +install: $(OUTFILE) + install -D -s $(OUTFILE) $(DESTDIR)/$(INSTALL_PATH)/$(OUTFILE) + + +# Rules for making a distribution tarball + +TDIR=lua-gd-$(VERSION) +DFILES=COPYING README luagd.c lua-gd.spec Makefile test_features.lua +dist: $(DISTFILES) + rm -f $(TDIR).tar.gz + mkdir $(TDIR) + mkdir -p $(TDIR)/doc $(TDIR)/demos $(TDIR)/debian + cp $(DFILES) $(TDIR) + cp demos/* $(TDIR)/demos/ + cp doc/* $(TDIR)/doc/ + cp debian/* $(TDIR)/debian/ + tar czf $(TDIR).tar.gz $(TDIR) + rm -rf $(TDIR) + +clean: + rm -f $(OUTFILE) gd.lo + rm -rf $(TDIR) $(TDIR).tar.gz + rm -f demos/out.png demos/out.gif demos/counter.txt + +.PHONY: all test install clean dist diff --git a/README b/README new file mode 100644 index 000000000..b91be781f --- /dev/null +++ b/README @@ -0,0 +1,60 @@ +Lua-GD, the gd bindings for the Lua Programming Language +(c) 2004-13 Alexandre Erwin Ittner + + ++ Introduction + +"gd" is a C graphics library created by Thomas Boutell that allows your +code to quickly draw complete images with lines, polygons, arcs, text, +multiple colors, cut and paste from other images, flood fills, read in +or write out images in the PNG, JPEG or GIF format. This is particularly +useful in World Wide Web applications, where PNG and JPEG are two of the +formats accepted for inline images by most browsers. gd does not provide +for every possible desirable graphics operation. It is not necessary or +desirable for gd to become a kitchen-sink graphics package, but it does +include most frequently requested features, including both truecolor +and palette images, resampling (smooth resizing of truecolor images) +and so forth. You can get more information about gd from it's homepage. + +Lua-GD is a "binding": a library that exports gd functions to the Lua +Programming Language, allowing you to use gd from Lua. The API was +NOT literally exported, but changed in a way that make it familiar to +Lua users. + +Lua-GD is a programming library, not a paint program. If you are looking +for that or are not familiar to the Lua Programming Language, you are +in the wrong place. + + ++ Documentation + +See doc/index.html for installation instructions and API documentation. + + ++ License + +Lua-GD is copyrighted free software, distributed under the MIT license +(the same used by Lua 5.1) and it can be used at no cost for both academic +and commercial purpouses. Or, more precisely: + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +If you use this package in a product, an acknowledgment in the product +documentation would be greatly appreciated (but it is not required). diff --git a/README.win32 b/README.win32 new file mode 100644 index 000000000..7250fb361 --- /dev/null +++ b/README.win32 @@ -0,0 +1,43 @@ +Lua-GD, the gd bindings for the Lua Programming Language +(c) 2004-11 Alexandre Erwin Ittner + +These are the Windows binaries for Lua-GD for Lua 5.1, compiled with +mingw32 3.4.4. These binaries also work with LuaJit 1.1.0. GD and its +dependencies were included in the distribution package for convenience. + +Please refer to 'README' For non-win32 specific information. + + ++ Licensing + +Each library has its own license. Please carefully read the following files: + + gd-license.txt + libfreetype-license.txt + libjpeg-license.txt + libpng-license.txt + zlib-license.txt + + ++ Instalation + +There is no automatic installation procedure for Windows. You must copy the +following DLLs to somewhere in your system path: + + freetype6.dll + jpeg62.dll + libgd2.dll + libiconv2.dll + libpng13.dll + xpm4.dll + zlib1.dll + +and, finally, copy the file "gd.dll" to your Lua binary package directory. + + ++ URLs + +Lua-GD: http://lua-gd.luaforge.net/ +Lua binaries: http://luabinaries.luaforge.net/ +GD binaries: http://gnuwin32.sourceforge.net/packages/gd.htm + diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 000000000..ddbdf815f --- /dev/null +++ b/debian/changelog @@ -0,0 +1,19 @@ +lua-gd (2.0.33r3-1) unstable; urgency=low + + * Update version. + + -- Alexandre Erwin Ittner Tue, 03 Oct 2013 01:00:00 -0300 + +lua-gd (2.0.33r2-2) unstable; urgency=low + + * Updated to Debian compatibility level 4. + + -- Alexandre Erwin Ittner Sun, 11 Feb 2007 15:20:09 -0200 + + +lua-gd (2.0.33r2-1) unstable; urgency=low + + * Initial release Closes: #nnnn (nnnn is the bug number of your ITP) + + -- Alexandre Erwin Ittner Wed, 3 May 2006 00:46:09 -0300 + diff --git a/debian/control b/debian/control new file mode 100644 index 000000000..5d86ea47f --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: lua-gd +Priority: optional +Maintainer: Alexandre Erwin Ittner +Build-Depends: debhelper (>= 4.0.0), liblua5.1-dev +Standards-Version: 3.6.1 +Section: interpreters + +Package: lua-gd +Architecture: any +Depends: ${shlibs:Depends} +Recommends: lua5.1 +Description: GD bindings for the Lua Programming Language + Lua-GD is a library that allows you to use the gd graphic library from + programs written in the Lua programming language. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 000000000..11befb5bc --- /dev/null +++ b/debian/copyright @@ -0,0 +1,23 @@ +Lua-GD (c) 2004-2006 Alexandre Erwin Ittner + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +If you use this package in a product, an acknowledgment in the product +documentation would be greatly appreciated (but it is not required). diff --git a/debian/rules b/debian/rules new file mode 100755 index 000000000..ddf911a6a --- /dev/null +++ b/debian/rules @@ -0,0 +1,83 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +## This is my first Debian package. Please, be patient ;) + + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This is the debhelper compatability version to use. +export DH_COMPAT=4 + +LUAPKG=lua5.1 +INSTALL_PATH=`pkg-config $(LUAPKG) --variable=INSTALL_CMOD` + +CFLAGS = -Wall -g + +build: build-stamp +build-stamp: + dh_testdir + +# Well, this does it all. + $(MAKE) + + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f gd.so + rm -f build-stamp configure-stamp + +# Toplevel clean does it all + -$(MAKE) clean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + mkdir -p debian/lua-gd/usr/share/doc/lua-gd + mkdir -p debian/lua-gd/usr/share/doc/lua-gd/demos/ + mkdir -p debian/lua-gd/$(INSTALL_PATH) + + cp gd.so debian/lua-gd/$(INSTALL_PATH) + cp README COPYING doc/* debian/lua-gd/usr/share/doc/lua-gd/ + cp demos/* debian/lua-gd/usr/share/doc/lua-gd/demos/ + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir -a + dh_testroot -a +# dh_installdebconf -a + dh_installdocs -a +# dh_installexamples -a +# dh_installmenu -a +# dh_installlogrotate -a +# dh_installemacsen -a +# dh_installpam -a +# dh_installmime -a +# dh_installinit -a +# dh_installcron -a +# dh_installman -a +# dh_installinfo -a +# dh_undocumented -a + dh_installchangelogs -a + dh_strip -a + dh_link -a + dh_compress -a + dh_fixperms -a + dh_makeshlibs -a -V + dh_installdeb -a +# dh_perl -a + dh_shlibdeps -a + dh_gencontrol -a + dh_md5sums -a + dh_builddeb -a + +binary: binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/demos/Vera.ttf b/demos/Vera.ttf new file mode 100644 index 000000000..58cd6b5e6 Binary files /dev/null and b/demos/Vera.ttf differ diff --git a/demos/brush.lua b/demos/brush.lua new file mode 100755 index 000000000..0c8530ded --- /dev/null +++ b/demos/brush.lua @@ -0,0 +1,20 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local im = gd.createTrueColor(400, 400) +assert(im) + +local black = im:colorAllocate(0, 0, 0) +local white = im:colorAllocate(255, 255, 255) + +local brush = gd.createFromPng("paper.png") +im:setBrush(brush) +im:line(60, 60, 70, 70, gd.BRUSHED) +im:line(120, 120, 130, 130, gd.BRUSHED) +im:line(180, 180, 190, 190, gd.BRUSHED) +im:line(240, 240, 250, 250, gd.BRUSHED) +im:line(300, 300, 310, 310, gd.BRUSHED) + +im:png("out.png") +os.execute("xdg-open out.png") diff --git a/demos/bugs.jpg b/demos/bugs.jpg new file mode 100644 index 000000000..f081055e2 Binary files /dev/null and b/demos/bugs.jpg differ diff --git a/demos/circle.lua b/demos/circle.lua new file mode 100755 index 000000000..18e4bd066 --- /dev/null +++ b/demos/circle.lua @@ -0,0 +1,14 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local im = gd.createTrueColor(100, 100) + +local white = im:colorAllocate(255, 255, 255) +local blue = im:colorAllocate(0, 0, 255) + +im:filledRectangle(0, 0, 100, 100, white) +im:stringFTCircle(50, 50, 40, 10, 0.9, "./Vera.ttf", 24, "Powered", "by Lua", blue) + +im:png("out.png") +os.execute("xdg-open out.png") diff --git a/demos/clock.lua b/demos/clock.lua new file mode 100755 index 000000000..0e99d4b42 --- /dev/null +++ b/demos/clock.lua @@ -0,0 +1,68 @@ +#!/usr/bin/env lua + +-- a cgi script that draws an analog clock with lua and lua-gd +-- (c) 2004 Alexandre Erwin Ittner + +local gd = require("gd") + +local function createClock(size, hours, minutes) + local im = gd.createTrueColor(size, size) + local white = im:colorAllocate(255, 255, 255) + local gray = im:colorAllocate(128, 128, 128) + local black = im:colorAllocate(0, 0, 0) + local blue = im:colorAllocate(0, 0, 128) + local cxy = size/2 + + im:filledRectangle(0, 0, size, size, white) + im:setThickness(math.max(1, size/100)) + im:arc(cxy, cxy, size, size, 0, 360, black) + + local ang = 0 + local rang, gsize + while ang < 360 do + rang = math.rad(ang) + if (ang % 90) == 0 then + gsize = 0.75 + else + gsize = 0.85 + end + im:line( + cxy + gsize * cxy * math.sin(rang), + size - (cxy + gsize * cxy * math.cos(rang)), + cxy + cxy * 0.9 * math.sin(rang), + size - (cxy + cxy * 0.9 * math.cos(rang)), + gray) + ang = ang + 30 + end + + im:setThickness(math.max(1, size/50)) + im:line(cxy, cxy, + cxy + 0.45 * size * math.sin(math.rad(6*minutes)), + size - (cxy + 0.45 * size * math.cos(math.rad(6*minutes))), + blue) + + im:setThickness(math.max(1, size/25)) + rang = math.rad(30*hours + minutes/2) + im:line(cxy, cxy, + cxy + 0.25 * size * math.sin(rang), + size - (cxy + 0.25 * size * math.cos(rang)), + blue) + + im:setThickness(1) + local sp = math.max(1, size/20) + im:filledArc(cxy, cxy, sp, sp, 0, 360, black, gd.ARC) + + return im +end + +local dh = os.date("*t") +local im = createClock(100, dh.hour, dh.min) + +print("Content-type: image/png") +print("Refresh: 60") -- Ask browser to reload the image after 60s +print("Pragma: no-cache") -- Can mozilla understand this? +print("Expires: Thu Jan 01 00:00:00 UTC 1970") -- Marks as expired +print("") + +io.write(im:pngStr()) + diff --git a/demos/colortransparent.lua b/demos/colortransparent.lua new file mode 100755 index 000000000..4d5f19fab --- /dev/null +++ b/demos/colortransparent.lua @@ -0,0 +1,16 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local im = gd.createPalette(90, 90) + +local white = im:colorAllocate(255, 255, 255) +local blue = im:colorAllocate(0, 0, 255) +local red = im:colorAllocate(255, 0, 0) + +im:colorTransparent(white) +im:filledRectangle(10, 10, 50, 50, blue) +im:filledRectangle(40, 40, 80, 80, red) + +im:gif("out.gif") +os.execute("xdg-open out.gif") diff --git a/demos/counter.lua b/demos/counter.lua new file mode 100755 index 000000000..885baad28 --- /dev/null +++ b/demos/counter.lua @@ -0,0 +1,30 @@ +#!/usr/bin/env lua +-- counter.lua -- a web counter in Lua! +-- (c) 2004 Alexandre Erwin Ittner + +local gd = require("gd") + +local datafile = "counter.txt" +local fp = io.open(datafile, "r+") +local cnt = 0 +if fp then + cnt = tonumber(fp:read("*l")) or 0 + fp:seek("set", 0) +else + fp = io.open(datafile, "w") + assert(fp) +end +cnt = cnt + 1 +fp:write(cnt .."\n") +fp:close() + +local sx = math.max(string.len(tostring(cnt)), 1) * 8 +local im = gd.create(sx, 15) +-- first allocated color defines the background. +local white = im:colorAllocate(255, 255, 255) +im:colorTransparent(white) +local black = im:colorAllocate(0, 0, 0) +im:string(gd.FONT_MEDIUM, 1, 1, cnt, black) + +print("Content-type: image/png\n") +io.write(im:pngStr()) diff --git a/demos/ellipse.lua b/demos/ellipse.lua new file mode 100755 index 000000000..40036c2ad --- /dev/null +++ b/demos/ellipse.lua @@ -0,0 +1,12 @@ +#!/usr/bin/env lua + +local gd = require("gd") +local im = gd.createTrueColor(80, 80) +assert(im) + +local black = im:colorAllocate(0, 0, 0) +local white = im:colorAllocate(255, 255, 255) +im:filledEllipse(40, 40, 70, 50, white) + +im:png("out.png") +os.execute("xdg-open out.png") diff --git a/demos/fontconfig.lua b/demos/fontconfig.lua new file mode 100755 index 000000000..34984695b --- /dev/null +++ b/demos/fontconfig.lua @@ -0,0 +1,23 @@ +#!/usr/bin/env lua + +-- The fonts used in this example comes with Microsoft operating systems +-- and can be downloaded from http://corefonts.sourceforge.net + +local gd = require("gd") + +local im = gd.createTrueColor(220, 190) +local white = im:colorAllocate(255, 255, 255) +local black = im:colorAllocate(0, 0, 0) +local x, y = im:sizeXY() +im:filledRectangle(0, 0, x, y, white) + +gd.useFontConfig(true) +im:stringFT(black, "Arial", 20, 0, 10, 30, "Standard Arial") +im:stringFT(black, "Arial:bold", 20, 0, 10, 60, "Bold Arial") +im:stringFT(black, "Arial:italic", 20, 0, 10, 90, "Italic Arial") +im:stringFT(black, "Arial:bold:italic", 20, 0, 10, 120, "Italic Bold Arial") +im:stringFT(black, "Times New Roman", 20, 0, 10, 150, "Times New Roman") +im:stringFT(black, "Comic Sans MS", 20, 0, 10, 180, "Comic Sans MS") + +im:png("out.png") +os.execute("xdg-open out.png") diff --git a/demos/fractal.lua b/demos/fractal.lua new file mode 100755 index 000000000..eaeac2225 --- /dev/null +++ b/demos/fractal.lua @@ -0,0 +1,31 @@ +#!/usr/bin/env lua + +-- Draws the famous Sierpinski triangle with lua-gd + +local gd = require("gd") + +local size = 500 + +local im = gd.createPalette(size, size) +local white = im:colorAllocate(255, 255, 255) +local black = im:colorAllocate(0, 0, 0) + +local m = {} +m[math.floor(size/2)] = true + +local n +for i = 1, size do + n = {} + for j = 1, size do + if m[j] then + im:setPixel(j, i, black) + n[j+1] = not n[j+1] + n[j-1] = not n[j-1] + end + end + m = n +end + +im:png("out.png") +os.execute("xdg-open out.png") + diff --git a/demos/gd-open-any.lua b/demos/gd-open-any.lua new file mode 100644 index 000000000..c7b39eb78 --- /dev/null +++ b/demos/gd-open-any.lua @@ -0,0 +1,52 @@ +-- +-- Open images automatically according to the file magic number. +-- Part of Lua-GD +-- + + +local gd = require("gd") +local io = require("io") + +local magics = { + { "\137PNG", gd.createFromPng }, + { "GIF87a", gd.createFromGif }, + { "GIF89a", gd.createFromGif }, + { "\255\216\255\224\0\16\74\70\73\70\0", gd.createFromJpeg }, + { "\255\216\255\225\19\133\69\120\105\102\0", gd.createFromJpeg }, -- JPEG Exif + } + +-- +-- Open some common image types according to the file headers +-- +-- Arguments: +-- fname: a string with the file name +-- +-- Return values: +-- on success, returns a GD image handler. +-- on error, returns nil followed by a string with the error description. +-- + +local function openany(fname) + local fp = io.open(fname, "rb") + if not fp then + return nil, "Error opening file" + end + + local header = fp:read(16) + if not header then + return nil, "Error reading file" + end + fp:close() + + for _, v in ipairs(magics) do + if header:sub(1, #v[1]) == v[1] then + return v[2](fname) + end + end + + return nil, "Image type not recognized" +end + + +return { openany = openany } + diff --git a/demos/gifanim.lua b/demos/gifanim.lua new file mode 100755 index 000000000..7cf0bebee --- /dev/null +++ b/demos/gifanim.lua @@ -0,0 +1,21 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local im = gd.createPalette(80, 80) +assert(im) + +local black = im:colorAllocate(0, 0, 0) +local white = im:colorAllocate(255, 255, 255) +im:gifAnimBegin("out.gif", true, 0) + +for i = 1, 10 do + tim = gd.createPalette(80, 80) + tim:paletteCopy(im) + tim:arc(40, 40, 40, 40, 36*(i-1), 36*i, white) + tim:gifAnimAdd("out.gif", false, 0, 0, 5, gd.DISPOSAL_NONE) +end + +gd.gifAnimEnd("out.gif") +os.execute("xdg-open out.gif") + diff --git a/demos/gifanim2.lua b/demos/gifanim2.lua new file mode 100755 index 000000000..ac128a82b --- /dev/null +++ b/demos/gifanim2.lua @@ -0,0 +1,24 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local im = gd.createPalette(120, 120) +assert(im) + +local black = im:colorAllocate(0, 0, 0) +local blue = {} +for i = 1, 20 do + blue[i] = im:colorAllocate(0, 0, 120+6*i) +end + +im:gifAnimBegin("out.gif", true, 0) +for i = 1, 20 do + tim = gd.createPalette(120, 120) + tim:paletteCopy(im) + tim:arc(60, 60, 6*i, 6*i, 0, 360, blue[21-i]) + tim:gifAnimAdd("out.gif", false, 0, 0, 5, gd.DISPOSAL_NONE) +end + +gd.gifAnimEnd("out.gif") +os.execute("xdg-open out.gif") + diff --git a/demos/gifanim3.lua b/demos/gifanim3.lua new file mode 100755 index 000000000..8eab3e103 --- /dev/null +++ b/demos/gifanim3.lua @@ -0,0 +1,29 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local im = gd.createPalette(120, 120) +assert(im) + +local black = im:colorAllocate(0, 0, 0) +local blue = {} +for i = 1, 20 do + blue[i] = im:colorAllocate(0, 0, 120+6*i) +end + +local fp = io.open("out.gif", "w") +assert(fp, "Failed to open file for writting") + +fp:write(im:gifAnimBeginStr(true, 0)) + +for i = 1, 20 do + tim = gd.createPalette(120, 120) + tim:paletteCopy(im) + tim:arc(60, 60, 6*i, 6*i, 0, 360, blue[21-i]) + fp:write(tim:gifAnimAddStr(false, 0, 0, 5, gd.DISPOSAL_NONE)) +end + +fp:write(gd.gifAnimEndStr()) +fp:close() + +os.execute("xdg-open out.gif") diff --git a/demos/lua-gd.png b/demos/lua-gd.png new file mode 100644 index 000000000..66412c8d4 Binary files /dev/null and b/demos/lua-gd.png differ diff --git a/demos/lualogo.lua b/demos/lualogo.lua new file mode 100755 index 000000000..e770b9c53 --- /dev/null +++ b/demos/lualogo.lua @@ -0,0 +1,60 @@ +#!/usr/bin/env lua + +-- +-- lualogo.lua (c) 2006-11 Alexandre Erwin Ittner +-- +-- Drawns the Lua Logo. This script requires fontconfig and the "Helvetica" +-- font installed in your system. +-- +-- + + +local gd = require("gd") + +gd.useFontConfig(true) + +function makelogo(size) + local nsize = 3 * size + local im = gd.createTrueColor(nsize, nsize) + local white = im:colorAllocate(255, 255, 255) + local blue = im:colorAllocate(0, 0, 128) + local gray = im:colorAllocate(170, 170, 170) + + local ediam = nsize * 0.68 -- Earth diameter + local mdiam = ediam * (1 - math.sqrt(2) / 2) -- Moon diameter + local odiam = ediam * 1.3 -- Orbit diameter + local emdist = odiam/2 * 1.05 -- Earth - Moon distance + local esdist = odiam/2 * 0.4 -- Earth - Moon shadow distance + local mang = 45 -- Moon angle (degrees) + local mangr = math.rad(mang) + local cxy = nsize/2.0 + + im:fill(0, 0, white) + im:filledArc(cxy, cxy, ediam, ediam, 0, 360, blue, gd.ARC) + + im:setThickness(math.max(0.02 * ediam, 1)) + for i = 0, 360, 10 do + im:arc(cxy, cxy, odiam, odiam, i, i+5, gray) + end + im:setThickness(1) + + -- Moon + local mcx = cxy + math.sin(math.rad(mang)) * emdist + local mcy = cxy - math.cos(math.rad(mang)) * emdist + im:filledArc(mcx, mcy, mdiam, mdiam, 0, 360, blue, gd.ARC) + + -- Moon shadow + local mscx = cxy + math.sin(math.rad(mang)) * esdist + local mscy = cxy - math.cos(math.rad(mang)) * esdist + im:filledArc(mscx, mscy, mdiam, mdiam, 0, 360, white, gd.ARC) + + im:stringFT(white, "Helvetica", 0.23*nsize, 0, 0.25*nsize, 0.7*nsize, "Lua") + + -- Implementation of the "Desperate anti-aliasing algorithm" ;) + local im2 = gd.createTrueColor(size, size) + im2:copyResampled(im, 0, 0, 0, 0, size, size, nsize, nsize) + return im2 +end + +makelogo(140):png("out.png") +os.execute("xdg-open out.png") diff --git a/demos/paper.png b/demos/paper.png new file mode 100644 index 000000000..a9092c444 Binary files /dev/null and b/demos/paper.png differ diff --git a/demos/poly.lua b/demos/poly.lua new file mode 100755 index 000000000..2a16cc16f --- /dev/null +++ b/demos/poly.lua @@ -0,0 +1,16 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local im = gd.createTrueColor(80, 80) +assert(im) + +local black = im:colorAllocate(0, 0, 0) +local white = im:colorAllocate(255, 255, 255) + +im:polygon( { { 10, 10 }, { 10, 20 }, { 20, 20 }, { 20, 10 } }, white) +im:filledPolygon( { { 30, 30 }, { 30, 40 }, { 40, 40 }, { 40, 30 } }, white) +im:openPolygon( { { 50, 50 }, { 50, 60 }, { 60, 60 }, { 60, 50 } }, white) + +im:png("out.png") +print(os.execute("xdg-open out.png")) diff --git a/demos/setstyle.lua b/demos/setstyle.lua new file mode 100755 index 000000000..4560c9fa5 --- /dev/null +++ b/demos/setstyle.lua @@ -0,0 +1,36 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local im = gd.createPalette(150, 100) +assert(im, "Failed to create new image.") + +local white = im:colorAllocate(255, 255, 255) +local red = im:colorAllocate(200, 0, 0) +local green = im:colorAllocate(0, 128, 0) +local blue = im:colorAllocate(0, 0, 128) + +local style = {} + +for i = 0, 10 do + style[#style+1] = red +end + +for i = 0, 5 do + style[#style+1] = blue +end + +for i = 0, 2 do + style[#style+1] = green +end + +im:setStyle(style) + +for i = 0, 100, 2 do + im:line(i, i, i+50, i, gd.STYLED) +end + +im:png("out.png") +os.execute("xdg-open out.png") + + diff --git a/demos/stdfont.lua b/demos/stdfont.lua new file mode 100755 index 000000000..42aafca3c --- /dev/null +++ b/demos/stdfont.lua @@ -0,0 +1,22 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local x, y = 140, 110 + +local im = gd.createPalette(x, y) +local white = im:colorAllocate(255, 255, 255) +local black = im:colorAllocate(0, 0, 0) + +im:string(gd.FONT_TINY, 10, 10, "gd.FONT_TINY", black) +im:string(gd.FONT_SMALL, 10, 20, "gd.FONT_SMALL", black) +im:string(gd.FONT_MEDIUM, 10, 35, "gd.FONT_MEDIUM", black) +im:string(gd.FONT_LARGE, 10, 48, "gd.FONT_LARGE", black) +im:string(gd.FONT_GIANT, 10, 65, "gd.FONT_GIANT", black) + +im:line(60, 93, 70, 93, black) +im:string(gd.FONT_SMALL, 80, 86, "= 10 px", black) + +im:png("out.png") +os.execute("xdg-open out.png") + diff --git a/demos/steg.lua b/demos/steg.lua new file mode 100755 index 000000000..a822bc1b5 --- /dev/null +++ b/demos/steg.lua @@ -0,0 +1,361 @@ +#!/usr/bin/env lua +--[[ + + Steganography with Lua-GD + +Steganography is the technique of writing hidden messages in such a way +that no one apart from the intended recipient knows of the existence of +the message; this is in contrast to cryptography, where the existence +of the message is clear, but the meaning is obscured. Generally a +steganographic message will appear to be something else, like a shopping +list, an article, a picture, or some other "cover" message. In the +digital age, steganography works by replacing bits of useless or unused +data in regular computer files (such as graphics, sound, text, HTML, or +even floppy disks) with bits of different, invisible information. This +hidden information can be plain text, cipher text or even images. + + + A Simple Example + +If Alice wants to send a secret message to Bob through an insecure +channel, she can use some encryption software (like GnuPG) to encrypt +the message with Bob's public key. It's a good solution because no +one unless Bob will be able to read the message. She can also sign the +message so Bob will know that the message really comes from her. BUT, +a potential attacker will know that a ciphered message was sent. If the +attacker has control over the communication channel, he might block the +message in some way that Bob will never receive it. If Alice also HIDES +the ciphertext in an unsuspected piece of information (like a photo of her +cat) the attacker will not detect it and the message will arrive to Bob. + +This program will help Alice to hide some arbitrary text in a PNG image by +replacing the least significant bits of each color channel of some pixels +with bits from the encrypted message. PNG or other loseless compression +algorithm are mandatory here, since compressing the image with a lossy +algorithm will destroy the stored information. The maximum length of the +message is limited by the image's size (each byte needs 8 color channels or +2 pixels and 2 channels from the next pixel). So, the image must have at +least "ceil((length+1)*8/3)" pixels (the extra byte is the NUL marker for +the end of the string). So, if Alice's message is "Meet me in the secret +place at nine o'clock.", she will encrypt and sign it to something like +"PyJYDpz5LCOSHPiXDvLHmVzxLV8qS7EFvZnoo1Mxk+BlT+7lMjpQKs" (imagine Alice's +cat walking in you keyboard :). This is the ciphertext that will be sent +to Bob through the image. + +The following table shows what happens to the first eight pixels from +the image when mixed to the first three bytes from the encrypted message: + + + +-----+---+----------+-----------------+----------+ + | Pix | C | Orig img | Message | New img | + | # | | bits | Chr | Dec | Bin | bits | + +-----+---+----------+-----+-----+-----+----------+ + | | R | 01010010 | | | 0 | 01010010 | + | 1 | G | 00101010 | | | 1 | 00101011 | + |_____| B | 00010101 | | | 0 | 00010100 | + | | R | 11100100 | P | 080 | 1 | 11100101 | + | 2 | G | 00100100 | | | 0 | 00100100 | + |_____| B | 01001111 | | | 0 | 01001110 | + | | R | 01010010 | | | 0 | 01010010 | + | 3 | G | 00101110 |_____|_____|__0__| 00101110 | + |_____| B | 00111001 | | | 0 | 00111000 | + | | R | 10010110 | | | 1 | 10010111 | + | 4 | G | 01011101 | | | 1 | 01011101 | + |_____| B | 00100101 | y | 121 | 1 | 00100101 | + | | R | 01001001 | | | 1 | 01001001 | + | 5 | G | 10110110 | | | 0 | 10110110 | + |_____| B | 00010101 | | | 0 | 00010100 | + | | R | 00110100 |_____|_____|__1__| 00110101 | + | 6 | G | 01000111 | | | 0 | 01000110 | + |_____| B | 01001000 | | | 1 | 01001001 | + | | R | 01010110 | | | 0 | 01010110 | + | 7 | G | 00011001 | | | 0 | 00011000 | + |_____| B | 10010100 | J | 074 | 1 | 10010101 | + | | R | 00010101 | | | 0 | 00010100 | + | 8 | G | 01011010 | | | 1 | 01011011 | + | | B | 01010001 | | | 0 | 01010000 | + +-----+---+----------+-----+-----+-----+----------+ + + +When Bob wants to read the message he will extract the least significant +bit (LSB) from each color channel from some pixels of the image and +join them to get the original ciphertext. A NULL character (ASCII #0) +will mark the end of the message within the image, so he will know when +to stop. Of course, this program will also do this boring job for Bob. + + +]] + + +local gd = require("gd") + + +local function getLSB(n) + return (n % 2) ~= 0 +end + + +-- Bizarre way to do some bit-level operations without bitlib. +local function setLSB(n, b) + if type(b) == "number" then + if b == 0 then + b = false + else + b = true + end + end + if getLSB(n) then + if b then + return n + elseif n > 0 then + return n - 1 + else + return n + 1 + end + else + if not b then + return n + elseif n > 0 then + return n - 1 + else + return n + 1 + end + end +end + + +local function intToBitArray(n) + local ret = {} + local i = 0 + while n ~= 0 do + ret[i] = getLSB(n) + n = math.floor(n/2) + ret.size = i + i = i + 1 + end + return ret +end + + +local function printBitArray(a) + local i + for i = a.size,0,-1 do + if a[i] then + io.write("1") + else + io.write("0") + end + end +end + + +local function mergeMessage(im, msg) + local w, h = im:sizeXY() + msg = msg .. string.char(0) + local len = string.len(msg) + if h * w < len * 8 then + return nil + end + local x, y = 0, 0 + local oim = gd.createTrueColor(w, h) + local i = 1 + local a2, c, nc, chr + local a = {} + local s, e = 1, 1 + local rgb = {} + + while y < h do + c = im:getPixel(x, y) + rgb.r = im:red(c) + rgb.g = im:green(c) + rgb.b = im:blue(c) + if i <= len and e - s < 3 then + a2 = intToBitArray(string.byte(string.sub(msg, i, i))) + for cnt = 7,0,-1 do + a[e+7-cnt] = a2[cnt] + end + i = i + 1 + e = e + 8 + end + if e - s > 0 then + rgb.r = setLSB(rgb.r, a[s]) + a[s] = nil + s = s + 1 + end + if e - s > 0 then + rgb.g = setLSB(rgb.g, a[s]) + a[s] = nil + s = s + 1 + end + if e - s > 0 then + rgb.b = setLSB(rgb.b, a[s]) + a[s] = nil + s = s + 1 + end + nc = oim:colorResolve(rgb.r, rgb.g, rgb.b) + oim:setPixel(x, y, nc) + x = x + 1 + if x == w then + x = 0 + y = y + 1 + end + end + return oim, len*8, w*h +end + + +local function getMessage(im) + local msg = {} + local w, h = im:sizeXY() + local x, y = 0, 0 + local a = {} + local s, e = 1, 1 + local b = 0 + local c + while y <= h do + c = im:getPixel(x, y) + a[e] = getLSB(im:red(c)) + a[e+1] = getLSB(im:green(c)) + a[e+2] = getLSB(im:blue(c)) + e = e + 2 + if e - s >= 7 then + b = 0 + for p = s, s+7 do + b = b * 2 + if a[p] then + b = b + 1 + end + a[p] = nil + end + s = s + 8 + if b == 0 then + return table.concat(msg) + else + msg[#msg+1] = string.char(b) + end + end + e = e + 1 + x = x + 1 + if x == w then + x = 0 + y = y + 1 + end + end + return table.concat(msg) +end + + +local function compare(fimg1, fimg2) + local im1 = gd.createFromPng(fimg1) + if not im1 then + print("ERROR: " .. fimg1 .. " bad PNG data.") + os.exit(1) + end + local im2 = gd.createFromPng(fimg2) + if not im2 then + print("ERROR: " .. fimg2 .. " bad PNG data.") + os.exit(1) + end + local w1, h1 = im1:sizeXY() + local w2, h2 = im2:sizeXY() + if w1 ~= w2 or h1 ~= h2 then + print("ERROR: Images have different sizes.") + os.exit(1) + end + local oim = gd.createTrueColor(w1, h1) + local x, y = 0, 0 + local c1, c2, oc, f, fc + while y < h1 do + c1 = im1:getPixel(x, y) + c2 = im2:getPixel(x, y) + if im1:red(c1) ~= im2:red(c2) + or im1:green(c1) ~= im2:green(c2) + or im1:blue(c1) ~= im2:blue(c2) then + oc = oim:colorResolve(im2:red(c2), im2:green(c2), im2:blue(c2)) + oim:setPixel(x, y, oc) + else + f = math.floor((im1:red(c1) + im1:green(c1) + im1:blue(c1))/6.0) + fc = oim:colorResolve(f, f, f) + oim:setPixel(x, y, fc) + end + x = x + 1 + if x == w1 then + x = 0 + y = y + 1 + end + end + return oim +end + + +local function usage() + print("Usage:") + print(" lua steg.lua hide ") + print(" lua steg.lua show ") + print(" lua steg.lua diff ") + print("") + print(" hide - Reads a message from stdin and saves into .") + print(" show - Reads a message from and prints it to stdout.") + print(" diff - Compares two images and writes the diff to .") + print("") + print(" WARNING: All files used here must be in the PNG format!") +end + + +if not arg[1] or not arg[2] then + usage() + os.exit(1) +end + +if arg[1] == "show" then + local im = gd.createFromPng(arg[2]) + if not im then + print("ERROR: Bad image data.") + os.exit(1) + end + io.write(getMessage(im)) + os.exit(0) +end + +if arg[1] == "hide" then + if not arg[3] then + usage() + os.exit(1) + end + local im = gd.createFromPng(arg[2]) + if not im then + print("ERROR: Bad image data.") + os.exit(1) + end + print("Type your message and press CTRL+D to finish.") + local msg = io.read("*a") + local oim, l, t = mergeMessage(im, msg) + if not oim then + print("ERROR: Image is too small for the message.") + os.exit(1) + end + if not oim:png(arg[3]) then + print("ERROR: Failed to write output file.") + os.exit(1) + end + print(string.format("DONE: %2.1f%% of the image used to store the message.", + 100.0*l/t)) + os.exit(0) +end + +if arg[1] == "diff" then + if not arg[3] and arg[4] then + usage() + os.exit(1) + end + local oim = compare(arg[2], arg[3]) + if not oim:png(arg[4]) then + print("ERROR: Failed to write output file.") + os.exit(1) + end + os.exit(0) +end + +usage() +os.exit(1) + diff --git a/demos/test.lua b/demos/test.lua new file mode 100755 index 000000000..8fa91effa --- /dev/null +++ b/demos/test.lua @@ -0,0 +1,35 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +math.randomseed(os.time()) + +local im = gd.createFromJpeg("./bugs.jpg") +assert(im) +local sx, sy = im:sizeXY() + +local im2 = gd.createTrueColor(2*sx, sy) +local black = im2:colorAllocate(0, 0, 0) +local white = im2:colorAllocate(255, 255, 255) +gd.copy(im2, im, 0, 0, 0, 0, sx, sy, sx, sy) + +local sx2, sy2 = im2:sizeXY() +im2:stringUp(gd.FONT_SMALL, 5, sy2-10, gd.VERSION, white) + +for i = 0, 14 do + for j = 0, 24 do + local rcl = im2:colorAllocate(math.random(255), math.random(255), + math.random(255)) + im2:filledRectangle(sx+20+j*10, i*20+40, sx+30+j*10, i*20+50, rcl) + end +end + +im2:string(gd.FONT_GIANT, sx+80, 10, "Powered by Lua", white) + +local blackTr = im2:colorAllocateAlpha(0, 0, 0, 80) +im2:stringFT(blackTr, "./Vera.ttf", 140, 0, 70, 130, "gd") +im2:stringFT(white, "./Vera.ttf", 45, math.pi/5, 340, 250, "FreeType") + + +im2:png("out.png") +os.execute("xdg-open out.png") diff --git a/demos/test2.lua b/demos/test2.lua new file mode 100755 index 000000000..93a575901 --- /dev/null +++ b/demos/test2.lua @@ -0,0 +1,19 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local im = gd.createFromJpeg("./bugs.jpg") +assert(im) + +local white = im:colorAllocate(255, 255, 255) +im:string(gd.FONT_MEDIUM, 10, 10, "Powered by", white) + +local imlua = gd.createFromPng("./lua-gd.png") +-- imlua:colorTransparent(imlua:getPixel(0, 0)) + +local sx, sy = imlua:sizeXY() +gd.copy(im, imlua, 10, 25, 0, 0, sx, sy, sx, sy) +im:string(gd.FONT_MEDIUM, 10, 330, "http://ittner.github.com/lua-gd/", white) + +im:png("out.png") +os.execute("xdg-open out.png") diff --git a/demos/ttftext.lua b/demos/ttftext.lua new file mode 100755 index 000000000..c5e21e030 --- /dev/null +++ b/demos/ttftext.lua @@ -0,0 +1,30 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +local function boxedtext(im, color, font, size, ang, x, y, text, bcolor) + local font = "./" .. font .. ".ttf" + local llx, lly, lrx, lry, urx, ury, ulx, uly = + im:stringFT(color, font, size, math.rad(ang), x, y, text) + im:polygon({ {llx, lly}, {lrx, lry}, {urx, ury}, {ulx, uly} }, bcolor) +end + +local im = gd.createTrueColor(400, 400) +assert(im) + +local black = im:colorAllocate(0, 0, 0) +local grayt = im:colorAllocateAlpha(255, 255, 255, 70) +local bluet = im:colorAllocateAlpha(0, 0, 250, 70) +local redt = im:colorAllocateAlpha(255, 0, 0, 0) +local greent = im:colorAllocateAlpha(0, 250, 0, 70) +local lbluet = im:colorAllocateAlpha(180, 180, 255, 70) +local yellowt = im:colorAllocateAlpha(240, 240, 0, 70) + +boxedtext(im, yellowt, "Vera", 300, 0, 60, 350, "A", bluet) +boxedtext(im, greent, "Vera", 80, 45, 60, 220, "Ithil", bluet) +boxedtext(im, redt, "Vera", 45, 90, 380, 300, "Lua-GD", bluet) +boxedtext(im, lbluet, "Vera", 36, 290, 160, 130, "FreeType", bluet) +boxedtext(im, grayt, "Vera", 26, 180, 390, 360, "Turn 180° before read", bluet) + +im:png("out.png") +os.execute("xdg-open out.png") diff --git a/demos/ttftextex.lua b/demos/ttftextex.lua new file mode 100755 index 000000000..797f17505 --- /dev/null +++ b/demos/ttftextex.lua @@ -0,0 +1,49 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +gd.useFontConfig(true) -- Use Fontconfig by default. + +local im = gd.createTrueColor(400, 400) +assert(im) + +local black = im:colorAllocate(0, 0, 0) +local grayt = im:colorAllocateAlpha(255, 255, 255, 80) +local blue = im:colorAllocate(0, 0, 250) +local red = im:colorAllocate(255, 0, 0) +local green = im:colorAllocate(0, 250, 0) +local lblue = im:colorAllocate(180, 180, 255) +local yellow = im:colorAllocate(240, 240, 0) + +im:stringFTEx(lblue, "Vera", 20, 0, 40, 40, "Half\nspace", + { linespacing = 0.5 } ) + +im:stringFTEx(red, "Vera", 20, 0, 140, 40, "Single\nspace", + { linespacing = 1.0 } ) + +im:stringFTEx(green, "Vera", 20, 0, 240, 40, "Double\nspace", + { linespacing = 2.0 } ) + +im:stringFTEx(yellow, "Vera", 40, 0, 80, 140, "Distorted!", + { hdpi = 96, vdpi = 30 } ) + + +local k = "Kerniiiiiiiiiiiiiiiiiing?" +print(im:stringFTEx(red, "Vera", 30, 0, 10, 200, k, {})) +print(im:stringFTEx(red, "Vera", 30, 0, 10, 240, k, + { disable_kerning = true } )) + +for i = 10, 400, 10 do + im:line(i, 170, i, 250, grayt) +end + + +local llX, llY, lrX, lrY, urX, urY, ulX, ulY, fontpath = + im:stringFTEx(lblue, "Vera", 20, 0, 50, 320, "This font comes from", + { return_font_path_name = true } ) + +im:string(gd.FONT_MEDIUM, 10, 340, fontpath, lblue) + + +im:png("out.png") +os.execute("xdg-open out.png") diff --git a/demos/utf-8.lua b/demos/utf-8.lua new file mode 100755 index 000000000..a0fafedc5 --- /dev/null +++ b/demos/utf-8.lua @@ -0,0 +1,35 @@ +#!/usr/bin/env lua +-- -*- coding: utf-8 -*- + +-- UTF-8 encoded unicode text. You must use an UTF-8 compatible text editor +-- to change this and a compatible Unicode font (FreeSerif is a good one). +-- +-- WARNING: Windows Notepad will add some prefixes, making this file an +-- invalid Lua script. + +local gd = require("gd") + +local text = [[ +⌠ ☾ Lua-GD +⌑ Unicode/UTF-8 +β†Ίβ†»β‡’βœ‡β˜’β˜£β˜  +β™œβ™žβ™β™›β™šβ™β™žβ™œ +β™Ÿβ™Ÿβ™Ÿβ™Ÿβ™Ÿβ™Ÿβ™Ÿβ™Ÿ +β™™β™™β™™β™™β™™β™™β™™β™™ +β™–β™˜β™—β™•β™”β™—β™˜β™– +]] + +local fontname = "FreeSerif" -- Common on Unix systems +-- local fontname = "Arial Unicode" -- Common on Windows systems + +gd.useFontConfig(true) + +local im = gd.createTrueColor(180, 180) +local white = im:colorAllocate(255, 255, 255) +local black = im:colorAllocate(0, 0, 0) +local x, y = im:sizeXY() +im:filledRectangle(0, 0, x, y, white) +im:stringFT(black, fontname, 16, 0, 10, 30, text) + +im:png("out.png") +os.execute("xdg-open out.png") diff --git a/doc/cat.png b/doc/cat.png new file mode 100644 index 000000000..2e9385c99 Binary files /dev/null and b/doc/cat.png differ diff --git a/doc/catdiff.png b/doc/catdiff.png new file mode 100644 index 000000000..e65da2e67 Binary files /dev/null and b/doc/catdiff.png differ diff --git a/doc/catmsg.png b/doc/catmsg.png new file mode 100644 index 000000000..3f6f2042d Binary files /dev/null and b/doc/catmsg.png differ diff --git a/doc/clock-example.png b/doc/clock-example.png new file mode 100644 index 000000000..852fe5954 Binary files /dev/null and b/doc/clock-example.png differ diff --git a/doc/fontconfig-example.png b/doc/fontconfig-example.png new file mode 100644 index 000000000..bc07563a8 Binary files /dev/null and b/doc/fontconfig-example.png differ diff --git a/doc/gifanim.gif b/doc/gifanim.gif new file mode 100644 index 000000000..804b025e5 Binary files /dev/null and b/doc/gifanim.gif differ diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 000000000..39a742824 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,2796 @@ + + + + + + + Lua-GD Reference Manual + + + + + + + + + + +
+ + Lua-GD Logo + +

Lua-GD 2.0.33r3

+
+ + +

Contents

+ + + +

Introduction

+ +

"gd" is a C graphics library created by +Thomas Boutell that allows +your code to quickly draw complete images with lines, polygons, arcs, +text, multiple colors, cut and paste from other images, flood fills, +read in or write out images in the PNG, JPEG or GIF format. This is +particularly useful in World Wide Web applications, where PNG and JPEG are +two of the formats accepted for inline images by most browsers. gd does +not provide for every possible desirable graphics operation. It is not +necessary or desirable for gd to become a kitchen-sink graphics package, +but it does include most frequently requested features, including both +truecolor and palette images, resampling (smooth resizing of truecolor +images) and so forth. You can get more information about gd from +it's homepage.

+ +

Lua-GD is a "binding": a library that exports gd functions to the Lua Programming Language, allowing you to +use gd from Lua. The API was not literally exported, but changed +in a way that make it familiar to Lua users.

+ +

Lua-GD is a programming library, not a paint program. If you are +looking for that or are not familiar to the Lua Programming Language, +you are in the wrong place.

+ +

This document, which was heavly based on gd manual, describes the API +and provides the information needed to use the library. Some experience +with Lua is required.

+ +

A note on version numbers: Lua-GD version numbers is in +the format "X.Y.ZrW", where X.Y.Z indicates the gd version and W the +binding version. So, the 2.0.33r1 version is the first binding +version for gd 2.0.33 and 2.0.33r2 has some improvements/bug +fixes/etc. but were based in the same gd version.

+ + + +

Licensing information

+ +

Lua-GD and gd have diferent licenses. In order to use Lua-GD in your +application, you need to accept both them.

+ +

Lua-GD License

+ +

Lua-GD is copyrighted free software, distributed under the +MIT license (the same used by Lua 5.1) and it can be used at no cost for +both academic and commercial purpouses. Or, more precisely:

+ + +
+Lua-GD (c) 2004-2013 Alexandre Erwin Ittner
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+If you use this package in a product, an acknowledgment in the product
+documentation would be greatly appreciated (but it is not required).
+
+ + +

gd License

+ +
+

Portions copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004 by Cold Spring Harbor Laboratory. Funded under Grant +P41-RR02188 by the National Institutes of Health.

+ +

Portions copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, +2004 by Boutell.Com, Inc.

+ +

Portions relating to GD2 format copyright 1999, 2000, 2001, 2002, +2003, 2004 Philip Warner.

+ +

Portions relating to PNG copyright 1999, 2000, 2001, 2002, 2003, +2004 Greg Roelofs.

+ +

Portions relating to gdttf.c copyright 1999, 2000, 2001, 2002, 2003, +2004 John Ellson (ellson@graphviz.org).

+ +

Portions relating to gdft.c copyright 2001, 2002, 2003, 2004 John +Ellson (ellson@graphviz.org).

+ +

Portions relating to JPEG and to color quantization copyright 2000, +2001, 2002, 2003, 2004, Doug Becker and copyright (C) 1994, 1995, 1996, +1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Thomas G. Lane. This +software is based in part on the work of the Independent JPEG Group. See +the file README-JPEG.TXT for more information.

+ +

Portions relating to GIF compression copyright 1989 by Jef Poskanzer +and David Rowley, with modifications for thread safety by Thomas +Boutell.

+ +

Portions relating to GIF decompression copyright 1990, 1991, 1993 by +David Koblas, with modifications for thread safety by Thomas Boutell.

+ +

Portions relating to WBMP copyright 2000, 2001, 2002, 2003, 2004 +Maurice Szmurlo and Johan Van den Brande.

+ +

Portions relating to GIF animations copyright 2004 Jaakko HyvΓ€tti +(jaakko.hyvatti@iki.fi)

+ +

Permission has been granted to copy, distribute and modify gd in any +context without fee, including a commercial application, provided that +this notice is present in user-accessible supporting documentation.

+ +

This does not affect your ownership of the derived work itself, and the +intent is to assure proper credit for the authors of gd, not to interfere +with your productive use of gd. If you have questions, ask. "Derived +works" includes all programs that utilize the library. Credit must be +given in user-accessible documentation.

+ +

This software is provided "AS IS." The copyright holders disclaim +all warranties, either express or implied, including but not limited +to implied warranties of merchantability and fitness for a particular +purpose, with respect to this code and accompanying documentation.

+ +

Although their code does not appear in the current release, the +authors also wish to thank Hutchison Avenue Software Corporation for +their prior contributions.

+ +
+ + + +

Development

+ +

Lua-GD is developed in GitHub. +Bug reports, patches, pull requests, ports to other systems, etc. are +welcome.

+ + + + +

Download and installation

+ +

The most recent stable packages are available in the +Lua-GD releases page +in GitHub and in the +mirror on Sourceforge. +There are also some pre-compiled binary packages available for Linux and some +other Unix systems.

+ +

After downloading, you need to compile the library. This process +requires GNU Make, gd and its dependencies (libpng, libjpeg, FreeType2, +fontconfig, etc.) installed on your system. Some components that may +be disabled on your gd installation will be disabled in Lua-GD too. To +compile on Unix, just unpack the distribution package, enter the directory +and type make in your favourite shell. If the compilation +was successful, you will see something like:

+ +
+gcc -o gd.so `gdlib-config --features |sed -e "s/GD_/-DGD_/g"`
+`gdlib-config --cflags` `pkg-config lua5.1 --cflags` -O3 -Wall -shared
+`gdlib-config --ldflags` `gdlib-config --libs` `pkg-config lua5.1 --libs`
+-lgd luagd.c
+lua test_features.lua
+Lua-GD version: lua-gd 2.0.33r3
+Lua-GD features:
+    PNG support ..................... Enabled
+    GIF support ..................... Enabled
+    JPEG support .................... Enabled
+    XPM/XBM support ................. Enabled
+    FreeType support ................ Enabled
+    Fontconfig support .............. Enabled
+
+ +

To install the library with Lua 5.1 and above just type +make install, as root.

+ +

Warning: This will work only in systems with pkg-config (which +includes the major Linux distributions). In other systems, you will need to +edit the Makefile and manually install the library, by copying the 'gd.so' +file to your Lua binary modules directory.

+ +

Compiling Lua-GD on Windows systems is almost, but not quite, +entirely unlike compiling it on Unix. You will need to edit the Makefile +by yourself.

+ +

A note to Debian/Ubuntu users: To enable the Fontconfig and XPM +support, you will need the libgd2-xpm and libgd2-xpm-dev +packages. The libgd2-noxpm does not have Fontconfig support.

+ + +

Installing with LuaRocks

+ +

The installation procedure described above can be automated with +LuaRocks. If you have it in your +system, just type sudo luarocks install luagd +to download, build, and install Lua-GD.

+ + +

Library loading and initialization

+ +

Lua-GD uses the Lua 5.1 package system that allows you to simply do a

+ +
local gd = require("gd")
+ +

call to load up the library.

+ +

Compatibility notice: Until version 2.0.33r2, Lua-GD +exported a global table called "gd" (as usually done for Lua 5.0 libraries). +Versions 2.0.33.r3 and newer dropped Lua 5.0 compatibility and no longers +changes the global environment, so an explicit attribution is necessary.

+ + + +

Lua-GD API

+ +

The Lua-GD API is not identical to the gd C API: it was changed in +a way that make it more familiar to Lua users and use the extra power +provided by Lua, at cost of some learning for those that already use +gd.

+ +

All the functions and constants are stored on a single table returned by +require and stored to a variable conventionally named +gd. Images are objects and they can use the +common colon notation for its methods, but you can also use a C-style +syntax. So, the following two lines are equivalent:

+ +
+local gray1 = im:colorResolve(128, 128, 128)        -- Using the colon notation
+local gray2 = gd.colorResolve(im, 128, 128, 128)    -- Using a C-style notation
+
+ +

Constants, functions and methods are described bellow.

+ + + +

Constants

+ +

gd.VERSION

+

A string with the version and some information about the library.

+ +

gd.MAX_COLORS

+

The constant 256. This is the maximum number of colors in a +palette-based PNG file according to the PNG standard, and is also the +maximum number of colors in a palette-based gd image. This of course +does not apply to truecolor images.

+ +

gd.GD2_FMT_RAW
+gd.GD2_FMT_COMPRESSED

+ +

Used when saving images to GD2 files with gd2 and +gd2Str to indicate the image file format. This format is not +intended for general purpose use and should never be used to distribute +images. It is a file format allowing pseudo-random access to large +image files. Its purpose is solely to allow very fast loading of parts +of images. You should use GD2_FMT_RAW for faster loading +(and bigger files) or GD2_FMT_COMPRESSED for smaller files (and slower +loading).

+ + +

gd.ARC
+gd.CHORD
+gd.PIE
+gd.NO_FILL
+gd.EDGED

+ +

These constants are used with the method filledArc +and can be added together. gd.ARC, gd.CHORD, +gd.PIE (synonym for gd.CHORD), +gd.NO_FILL and gd.EDGED are mutually +exclusive; gd.CHORD just connects the starting and +ending angles with a straight line, while gd.ARC +produces a rounded edge. gd.PIE is a synonym for +gd.ARC. gd.NO_FILL indicates that the arc +or chord should be outlined, not filled. gd.EDGED, used +together with gd.NO_FILL, indicates that the beginning and +ending angles should be connected to the center; this is a good way to +outline (rather than fill) a 'pie slice'. See gd.filledArc +for details.

+ + +

gd.ANTI_ALIASED

+ +

Used in place of a color when invoking a line-drawing method +such as gd.line or gd.rectangle. When +gd.ANTI_ALIASED is used as the color, the foreground color +set with gd.setAntiAliased is used, with antialiasing +mechanisms to minimize any "jagged" appearance. For more information, +see gd.setAntiAliased.

+ + +

gd.BRUSHED

+ +

Used in place of a color when invoking a line-drawing method +such as gd.line or gd.rectangle. When +gd.BRUSHED is used as the color, the brush image set with +gd.setBrush is drawn in place of each pixel of the line +(the brush is usually larger than one pixel, creating the effect of a +wide paintbrush). See also gd.STYLED_BRUSHED for a way to +draw broken lines with a series of distinct copies of an image.

+ + +

gd.STYLED

+ +

Used in place of a color when invoking a line-drawing method +such as gd.line or gd.rectangle. When +gd.STYLED is used as the color, the colors of the +pixels are drawn successively from the style that has been set +with gd.setStyle. If the color of a pixel is equal to +gd.TRANSPARENT, that pixel is not altered. (This mechanism +is completely unrelated to the "transparent color" of the image itself; +see gd.colorTransparent for that mechanism).

+ + +

gd.STYLED_BRUSHED

+ +

Used in place of a color when invoking a line-drawing method +such as gd.line or gd.rectangle. When +gd.STYLED_BRUSHED is used as the color, the brush image +set with gd.setBrush is drawn at each pixel of the line, +providing that the style set with gd.setStyle contains +a nonzero value (OR gd.TRANSPARENT, which does not equal +zero but is supported for consistency) for the current pixel. (Pixels +are drawn successively from the style as the line is drawn, returning to +the beginning when the available pixels in the style are exhausted). Note +that this differs from the behavior of gd.STYLED, in which +the values in the style are used as actual pixel colors, except for +gd.TRANSPARENT.

+ + +

gd.TILED

+ +

Used in place of a normal color in gd.filledRectangle, +gd.filledPolygon, gd.fill, and + gd.fillToBorder. gd.TILED +selects a pixel from the tile image set with gd.setTile +in such a way as to ensure that the filled area will be tiled with +copies of the tile image. See the discussions of gd.fill +and gd.fillToBorder for special restrictions regarding +those methods.

+ + +

gd.TRANSPARENT

+ +

Used in place of a normal color in a style to be set with +gd.setStyle. gd.TRANSPARENT is not +the transparent color index of the image; for that functionality please +see gd.colorTransparent.

+ + +

gd.FONT_TINY
+gd.FONT_SMALL
+gd.FONT_MEDIUM
+gd.FONT_LARGE
+gd.FONT_GIANT

+ +

These are the standard gd fonts used by gd.string and +gd.stringUp. Sizes and styles are shown bellow:
+ +Standard gd fonts
+ +If you need another fonts, you should use TrueType fonts with +gd.stringFT. +

+ +

gd.FTEX_Unicode
+gd.FTEX_Shift_JIS
+gd.FTEX_Big5

+ +

These are the character maps used by gd.stringFTEx. Explicit +specification of the desired character mapping is useful when a font offers +more than one of Unicode, Shift_JIS, and Big5.

+ + +

Image creation and destruction

+ + +

gd.create(x, sy)
+gd.createPalette(x, y)

+ +

These functions are used to create palette-based images, with +no more than 256 colors. Invoke gd.create with the x +and y dimensions of the desired image to return the new image or +nil on error. gd.createPalette is a synomym for +gd.create.

+ +

Example:

+ +
+-- creates a 20x20px palette-based image
+local im = gd.create(20, 20)
+
+ +

You don't need to destroy an image by yourself. Lua has a garbage +collector that detects when an piece of information becomes useless and +"automagically" removes it from memory.

+ + +

gd.createTrueColor(x, y)

+ +

Used to create truecolor images, with an essentially unlimited +number of colors. Invoke gd.createTrueColor with the x +and y dimensions of the desired image to return the new image or nil +on error. Truecolor images are always filled with black at creation +time. There is no concept of a "background" color index.

+ +

Example:

+
+-- creates a 20x20px true color image
+local im = gd.createTrueColor(20, 20)
+
+ +

You don't need to destroy an image by yourself. Lua has a garbage +collector that detects when an piece of information becomes useless and +"automagically" deletes it from memory.

+ + + +

gdImage:createPaletteFromTrueColor(dither, colorsWanted)

+ +

This method creates a new palette-based image from a truecolor +image using a high-quality two-pass quantization routine. If +the dither flag is set to true, the image will be +dithered to approximate colors better, at the expense of some obvious +"speckling". colorsWanted, an integer value, can be anything up +to 256. If the original source image includes photographic information +or anything that came out of a JPEG, 256 is strongly recommended. 100% +transparency of a single transparent color in the original truecolor +image will be preserved. There is no other support for preservation of +alpha channel or transparency in the destination image.

+ +

For best results, don't use this method -- write real truecolor +PNGs and JPEGs. The disk space gain of conversion to palette is not great +(for small images it can be negative) and the quality loss is ugly.

+ + + + +

Image input functions

+ + +

gd.createFromJpeg(filename)

+ +

gd.createFromJpeg is called to load truecolor images from +JPEG format files. Invoke gd.createFromJpeg with a string +pointing to an existing file containing the desired image to return a new +truecolor image or nil if unable to load the image (most often +because the file is corrupt or does not contain a JPEG image). You can +determine the size of the image with gd.sizeXY. The returned +image is always a truecolor image.

+ + + +

gd.createFromPng(filename)

+ +

gd.createFromPng is called to load images from PNG format +files. Invoke gd.createFromPng with a string pointing to +an existing file containing the desired image to return a new image or +nil if unable to load the image (most often because the file is +corrupt or does not contain a PNG image). You can determine the size of +the image with gd.sizeXY.

+ +

If the PNG image being loaded is a truecolor image, the resulting +value will refer to a truecolor image. If the PNG image being loaded +is a palette or grayscale image, the resulting data will refer to a +palette image. gd retains only 8 bits of resolution for each of the red, +green and blue channels, and only 7 bits of resolution for the alpha +channel. The former restriction affects only a handful of very rare +48-bit color and 16-bit grayscale PNG images. The second restriction +affects all semitransparent PNG images, but the difference is essentially +invisible to the eye. 7 bits of alpha channel resolution is, in practice, +quite a lot.

+ +

Example:

+
+-- copies the content of picture.png to a new image
+local im = gd.createFromPng("picture.png")
+
+ + +

gd.createFromGif(filename)

+ +

gd.createFromGif is called to load images from GIF format +files. Invoke gd.createFromGif with a string pointing to +an existing file containing the desired image to return a new image or +nil if unable to load the image (most often because the file is +corrupt or does not contain a GIF image). You can determine the size of +the image with gd.sizeXY.

+ + +

gd.createFromGd(filename)

+ +

gd.createFromGd is called to load images from gd format +files. Invoke gd.createFromGd with a string pointing to +an existing file containing the desired image to return a new image or +nil if unable to load the image (most often because the file is +corrupt or does not contain a gd image). You can determine the size of +the image with gd.sizeXY. This format is not intended for +general purpose use and should never be used to distribute images. Its +purpose is solely to allow very fast loading of images.

+ + +

gd.createFromGd2(filename)

+ +

gd.createFromGd2 is called to load images from gd2 format +files. Invoke gd.createFromGd2 with a string pointing to +an existing file containing the desired image to return a new image or +nil if unable to load the image (most often because the file is +corrupt or does not contain a gd2 image). You can determine the size of +the image with gd.sizeXY. This format is not intended for +general purpose use and should never be used to distribute images. Its +purpose is solely to allow very fast loading of images.

+ + +

gd.createFromGd2Part(filename, x, y, w, h)

+ +

gd.createFromGd2Part is called to load parts +of images from gd2 format files. Invoked in the same way as +gd.createFromGd2, but with extra parameters indicating the +source (x, y) and width/height of the desired image.

+ +

Example:

+
+-- Being base.gd2 a 40x40px image, copies its upper-left part to a new image
+local im = gd.createFromGd2Part("base.gd2", 0, 0, 20, 20)
+
+ + +

gd.createFromXbm(filename)

+ +

gd.createFromXbm is called to load images from +monochromatic X bitmap format files. Invoke gd.createFromXbm +with a string pointing to an existing file containing the desired image +to return a new image or nil if unable to load the image (most +often because the file is corrupt or does not contain a XBM image). You +can determine the size of the image with gd.sizeXY.

+ + +

gd.createFromXpm(filename)

+ +

gd.createFromXpm is called to load images from color X +bitmap format files. Invoke gd.createFromXpm with a string +pointing to an existing file containing the desired image to return a +new image or nil if unable to load the image (most often because +the file is corrupt or does not contain a XPM image). You can determine +the size of the image with gd.sizeXY.

+ + +

gd.createFromJpegStr(string)

+ +

gd.createFromJpegStr is called to load +truecolor images from strings with JPEG format image data. Invoke +gd.createFromJpegStr passing a string with the desired +image to return a new truecolor image or nil if unable to +load the image (most often because the data is corrupt or does not +contain a JPEG image). You can determine the size of the image with +gd.sizeXY. The returned image is always a truecolor +image.

+ + +

gd.createFromGifStr(string)

+ +

gd.createFromGifStr is called to load +palette-based images from strings with GIF format image data. Invoke +gd.createFromGifStr passing a string with the desired image +to return a new image or nil if unable to load the image (most +often because the data is corrupt or does not contain a GIF image). You +can determine the size of the image with gd.sizeXY.

+ + +

gd.createFromPngStr(string)

+ +

gd.createFromPngStr is called to load images from strings +with PNG format image data. Invoke gd.createFromPngStr +passing a string with the desired image to return a new image or +nil if unable to load the image (most often because the data is +corrupt or does not contain a PNG image). You can determine the size of +the image with gd.sizeXY.

+ +

If the PNG image being loaded is a truecolor image, the resulting +value will refer to a truecolor image. If the PNG image being loaded +is a palette or grayscale image, the resulting data will refer to a +palette image. gd retains only 8 bits of resolution for each of the red, +green and blue channels, and only 7 bits of resolution for the alpha +channel. The former restriction affects only a handful of very rare +48-bit color and 16-bit grayscale PNG images. The second restriction +affects all semitransparent PNG images, but the difference is essentially +invisible to the eye. 7 bits of alpha channel resolution is, in practice, +quite a lot.

+ +

Example:

+ +
+-- reads the contents of the file to a string
+fp = io.open("picture.png")
+str = fp:read("*a")
+fp:close()
+
+-- creates an image from the string
+local im = gd.createFromPngStr(str)
+
+ + +

gd.createFromGdStr(string)

+ +

gd.createFromGdStr is called to load images from strings +with gd format image data. Invoke gd.createFromGdStr passing +a string with the desired image to return a new image or nil +if unable to load the image (most often because the data is corrupt or +does not contain a gd image). You can determine the size of the image +with gd.sizeXY. This format is not intended for general +purpose use and should never be used to distribute images. Its purpose +is solely to allow very fast loading of images.

+ + +

gd.createFromGd2Str(string)

+ +

gd.createFromGd2Str is called to load images from strings +with gd2 format image data. Invoke gd.createFromGd2Str +passing a string with the desired image to return a new image or +nil if unable to load the image (most often because the data is +corrupt or does not contain a gd2 image). You can determine the size of +the image with gd.sizeXY. This format is not intended for +general purpose use and should never be used to distribute images. Its +purpose is solely to allow very fast loading of images.

+ + +

gd.createFromGd2PartStr(string, x, y, w, h)

+ +

gd.createFromGd2PartStr is called to load parts +of images from gd2 format strings. Invoked in the same way as +gd.createFromGd2Str, but with extra parameters indicating +the source (x, y) and width/height of the desired image.

+ + +

Image output methods

+ +

gdImage:png(filename)

+ +

Outputs the image to a PNG file with the specified name (overwriting +existing files). This method returns false on error (eg. permission +denied to overwrite an existing file) and true on success.

+ +

Example:

+ +
+-- creates a new image
+local im = gd.createTrueColor(20, 20)
+
+-- ** DO SOME ART HERE **
+
+-- writes the image to a PNG file.
+im:png("picture.png")
+
+ + +

If you want to check if the writting was succeful:

+
+-- creates a new image
+local im = gd.createTrueColor(20, 20)
+
+-- ** DO SOME ART HERE **
+
+if im:png("picture.png") then
+  print("Image written to disk")
+else
+  print("Oops, an error...")
+end
+
+ + +

gdImage:pngEx(filename, compression_level)

+ +

Similiar to gd.png, but allows you to specify the +compression level of a PNG image, from 1 to 6 (or -1 for the default +compression level of libpng). This method returns false on error +(eg. permission denied to overwrite an existing file) and true +on success.

+ + +

gdImage:jpeg(filename, quality)

+ +

Outputs the image to a JPEG file with the specified name (overwriting +existing files). You must specify the quality of the generated JPEG +file from 1 (worst quality, small files) to 100 (best quality, large +files). This method returns false on error (eg. permission denied +to overwrite an existing file) and true on success.

+ + + +

gdImage:gif(filename)

+ +

Outputs the image to a GIF file with the specified name (overwriting +existing files). This method returns false on error (eg. permission +denied to overwrite an existing file) and true on success.

+ + +

gdImage:gd(filename)

+ +

Outputs the image to a gd format file with the specified name +(overwriting existing files). This method returns false on error +(eg. permission denied to overwrite an existing file) and true +on success. This format is not intended for general purpose use and +should never be used to distribute images. Its purpose is solely to +allow very fast loading of images.

+ + +

gdImage:gd2(filename, chunkSize, format)

+ +

Outputs the image to a gd2 format file with the specified name +(overwriting existing files). This method returns false on error +(eg. permission denied to overwrite an existing file) and true +on success. You must specify the chunk size (an integer, or 0 for the +default) and the file format, which can be gd.GD2_FMT_RAW +or gd.GD2_FMT_COMPRESSED. This format is not intended for +general purpose use and should never be used to distribute images. Its +purpose is solely to allow very fast loading of images.

+ + +

gdImage:wbmp(filename, fg)

+ +

Outputs the image to a WBMP file with the specified name (overwriting +existing files). WBMP file support is black and white only. The color +index specified by the fg argument is the "foreground", and only pixels +of this color will be set in the WBMP file. All other pixels will be +considered "background". This method returns false on error +(eg. permission denied to overwrite an existing file) and true +on success.

+ + +

gdImage:pngStr()

+ +

This method returns the image in the PNG format as a Lua string +or nil on error (eg. a corrupted image).

+ + +

gdImage:pngStrEx(compression_level)

+ +

Similiar to gd.pngStr, but allows you to specify the +compression level of a PNG image, from 1 to 6 (or -1 for the default +compression level of libpng). This method returns nil on +error.

+ + +

gdImage:jpegStr(quality)

+ +

This method returns the image in the JPEG format as a Lua string +or nil on error (eg. a corrupted image). You must specify the +quality of the generated JPEG data from 1 (worst quality, small string) +to 100 (best quality, large string).

+ +

Example:

+
+-- creates a new image
+local im = gd.createTrueColor(20, 20)
+
+-- ** DO SOME ART HERE **
+-- writes the image to a PNG file.
+im:png("picture.png")
+
+-- writes the image to stdout in the JPEG format
+io.write(im:jpegStr(75))
+
+ + + +

gdImage:gifStr()

+ +

This method returns the image in the GIF format as a Lua string +or nil on error (eg. a corrupted image).

+ + +

gdImage:gdStr()

+ +

This method returns the image in the gd format as a Lua string +or nil on error (eg. a corrupted image).

+ + +

gdImage:gd2Str(chunkSize, format)

+ +

This method returns the image in the gd2 format as a Lua string or +nil on error (eg. a corrupted image). You must specify the chunk +size (an integer, or 0 for the default) and the data format, which can be +gd.GD2_FMT_RAW or gd.GD2_FMT_COMPRESSED. This +format is not intended for general purpose use and should never be used +to distribute images. Its purpose is solely to allow very fast loading +of images.

+ + +

gdImage:wbmpStr(fg)

+ +

This method returns the image in the WBMP format as a Lua string +or nil on error (eg. a corrupted image). WBMP format support is +black and white only. The color index specified by the fg argument is +the "foreground", and only pixels of this color will be set in the WBMP +string. All other pixels will be considered "background".

+ + + +

Color manipulation methods

+ + +

gdImage:colorAllocate(red, green, blue)

+ +

colorAllocate() finds the first available color +index in the image specified, sets its RGB values to those requested +(255 is the maximum for each), and returns the index of the new color +table entry, or an RGBA value in the case of a truecolor image; in +either case you can then use the returned value as a parameter to +drawing methods. When creating a new palette-based image, the first +time you invoke this method, you are setting the background color for +that image.

+ +

In the event that all gd.MAX_COLORS colors (256) +have already been allocated, colorAllocate will +return nil to indicate failure. (This is not uncommon when working +with existing PNG files that already use 256 colors). Note that +colorAllocate does not check for existing colors that match +your request; see colorExact, colorClosest +and colorClosestHWB for ways to locate existing colors +that approximate the color desired in situations where a new color is +not available.

+ + +

gdImage:colorAllocateAlpha(red, green, blue, alpha)

+ +

colorAllocateAlpha finds the first available color index +in the image specified, sets its RGBA values to those requested (255 is +the maximum for red, green and blue, and 127 represents full transparency +for alpha), and returns the index of the new color table entry, or an RGBA +value in the case of a truecolor image; in either case you can then use +the returned value as a parameter to drawing methods. When creating +a new palette-based image, the first time you invoke this method, +you are setting the background color for that image.

+ +

In the event that all gd.MAX_COLORS colors (256) have +already been allocated, colorAllocateAlpha will return +nil to indicate failure. (This is not uncommon when working with +existing palette-based PNG files that already use 256 colors). Note +that colorAllocateAlpha does not check for existing +colors that match your request; see colorExactAlpha and +colorClosestAlpha for ways to locate existing colors that +approximate the color desired in situations where a new color is not +available. Also see colorResolveAlpha.

+ + + +

gdImage:colorClosest(red, green, blue)

+ +

colorClosest searches the colors which have been defined +thus far in the image specified and returns the index of the color with +RGB values closest to those of the request. (Closeness is determined +by Euclidian distance, which is used to determine the distance in +three-dimensional color space between colors).

+ +

If no colors have yet been allocated in the image, +colorClosest returns nil.

+ +

When applied to a truecolor image, this method always succeeds in +returning the desired color.

+ +

This method is most useful as a backup method for choosing a +drawing color when an image already contains gd.MAX_COLORS +(256) colors and no more can be allocated. (This is not uncommon when +working with existing PNG files that already use many colors). See +colorExact for a method of locating exact matches only.

+ + +

gdImage:colorClosestAlpha(red, green, blue, alpha)

+ +

colorClosestAlpha searches the colors which have been +defined thus far in the image specified and returns the index of the +color with RGBA values closest to those of the request. (Closeness is +determined by Euclidian distance, which is used to determine the distance +in four-dimensional color/alpha space between colors).

+ +

If no colors have yet been allocated in the image, +colorClosestAlpha returns nil.

+ +

When applied to a truecolor image, this method always succeeds in +returning the desired color.

+ +

This method is most useful as a backup method for +choosing a drawing color when a palette-based image already +contains gd.MAX_COLORS (256) colors and no more can be +allocated. (This is not uncommon when working with existing palette-based +PNG files that already use many colors). See colorExactAlpha +for a method of locating exact matches only.

+ + +

gdImage:colorClosestHWB(red, green, blue)

+ +

colorClosestHWB searches the colors which have been +defined thus far in the image specified and returns the index of the color +with hue, whiteness and blackness closest to the requested color. This +scheme is typically superior to the Euclidian distance scheme used by +colorClosest.

+ +

If no colors have yet been allocated in the image, +colorClosestHWB returns nil.

+ +

When applied to a truecolor image, this method always succeeds in +returning the desired color.

+ +

This method is most useful as a backup method for choosing a +drawing color when an image already contains gd.MAX_COLORS +(256) colors and no more can be allocated. (This is not uncommon when +working with existing PNG files that already use many colors). See +colorExact for a method of locating exact matches only.

+ + +

gdImage:colorExact(red, green, blue)

+ +

colorExact searches the colors which have been defined +thus far in the image specified and returns the index of the first +color with RGB values which exactly match those of the request. If no +allocated color matches the request precisely, colorExact +returns nil. See colorClosest for a way to find the +color closest to the color requested.

+ +

When applied to a truecolor image, this method always succeeds in +returning the desired color.

+ + +

gdImage:colorExactAlpha(red, green, blue, alpha)

+ +

colorExactAlpha searches the colors which have +been defined thus far in the image specified and returns the +index of the first color with RGBA values which exactly match +those of the request. If no allocated color matches the request +precisely, colorExactAlpha returns nil. See +colorClosestAlpha for a way to find the color closest to +the color requested.

+ +

When applied to a truecolor image, this method always succeeds in +returning the desired color.

+ +

gdImage:colorResolve(red, green, blue)

+ +

colorResolve searches the colors which have been defined +thus far in the image specified and returns the index of the first color +with RGB values which exactly match those of the request. If no allocated +color matches the request precisely, then colorResolve +tries to allocate the exact color. If there is no space left in +the color table then ColorResolve returns the closest color (as in +colorClosest). This method always returns an index of a +color.

+ +

When applied to a truecolor image, this method always succeeds in +returning the desired color.

+ + +

gdImage:colorResolveAlpha(red, green, blue, alpha)

+ +

colorResolveAlpha searches the colors which have +been defined thus far in the image specified and returns the index +of the first color with RGBA values which exactly match those of the +request. If no allocated color matches the request precisely, then +colorResolveAlpha tries to allocate the exact color. If there +is no space left in the color table then colorResolveAlpha +returns the closest color (as in colorClosestAlpha). This +method always returns an index of a color.

+ +

When applied to a truecolor image, this method always succeeds in +returning the desired color.

+ + +

gdImage:colorsTotal()

+

This method returns the number of colors currently allocated in a +palette image. For truecolor images, the result of this call is undefined +and should not be used.

+ + +

gdImage:red(color)

+

This method returns the red portion of the specified color in the +image. This method works for both palette and truecolor images.

+ + +

gdImage:blue(color)

+

This method returns the blue portion of the specified color in the +image. This method works for both palette and truecolor images.

+ + +

gdImage:green(color)

+

This method returns the green portion of the specified color in the +image. This method works for both palette and truecolor images.

+ + +

gdImage:alpha(color)

+ +

This method returns the alpha portion of the specified color in the +image. This method works for both palette and truecolor images.

+ + +

gdImage:getTransparent()

+

This method returns the current transparent color index in +the image. If there is no transparent color, getTransparent +returns nil.

+ + +

gdImage:colorTransparent(color)

+ +

colorTransparent sets the transparent color index for +the specified image to the specified index. To indicate that there +should be no transparent color, invoke colorTransparent +with a color index of nil (or passing no color index). Note that +JPEG images do not support transparency, so this setting has no effect +when writing JPEG images.

+ +

The color index used should be an index allocated by +colorAllocate, whether explicitly invoked by your code or +implicitly invoked by loading an image. In order to ensure that your +image has a reasonable appearance when viewed by users who do not +have transparent background capabilities (or when you are writing a +JPEG-format file, which does not support transparency), be sure to give +reasonable RGB values to the color you allocate for use as a transparent +color, even though it will be transparent on systems that support PNG +transparency.

+ + +

gdImage:colorDeallocate(color)

+ +

colorDeallocate marks the specified color as being +available for reuse. It does not attempt to determine whether the +color index is still in use in the image. After a call to this +method, the next call to colorAllocate for the same +image will set new RGB values for that color index, changing the color +of any pixels which have that index as a result. If multiple calls to +colorDeallocate are made consecutively, the lowest-numbered +index among them will be reused by the next colorAllocate +call.

+ + +

Query methods

+ +

gdImage:boundsSafe(x, y)

+ +

boundsSafe returns true if the specified point +is within the current clipping rectangle, false if not. The +clipping rectangle is set by setClip and defaults to the +entire image. This method is intended primarily for use by those who +wish to add methods to Lua-GD. All of the drawing functions already +clip safely.

+ + +

gdImage:getPixel(x, y)

+

getPixel returns the color index of a particular pixel.

+ +

gdImage:sizeX()

+

sizeX returns the width of the image in pixels.

+ + +

gdImage:sizeY()

+

sizeY returns the height of the image in pixels.

+ + +

gdImage:sizeXY()

+

sizeXY returns the width and the height of the image +in pixels. This method has no C equivalent because, unlike Lua, +C functions cannot return multiple values.

+ +

Example:

+
+local x, y = im:sizeXY()
+
+ + + +

gdImage:getClip()

+

Fetches the boundaries of the current clipping rectangle. This method +returns four numbers.

+ +

Example:

+
+local x1, y1, x2, y2 = im:getClip()
+
+ + + +

gdImage:setClip(x1, y1, x2, y2)

+ +

Establishes a clipping rectangle. Once setClip has been +called, all future drawing operations will remain within the specified +clipping area, until a new setClip call takes place. For +instance, if a clipping rectangle of 25, 25, 75, 75 has been set within +a 100x100 image, a diagonal line from 0,0 to 99,99 will appear only +between 25,25 and 75,75.

+ + +

Drawing methods

+ +

gdImage:setPixel(x, y, color)

+

setPixel sets a pixel to a particular color index.

+ +

gdImage:line(x1, y1, x2, y2, color)

+ +

line is used to draw a line between two endpoints +(x1, y1) and (x2, y2). The line is drawn using the color index +specified. Note that the color index can be an actual color returned +by colorAllocate or one of gd.STYLED, +gd.BRUSHED or gd.STYLED_BRUSHED.

+ + +

gdImage:rectangle(x1, y1, x2, y2, color)

+ +

rectangle is used to draw a rectangle with the two +corners (upper left first, then lower right) specified, using the color +index specified.

+ + +

gdImage:filledRectangle(x1, y1, x2, y2, color)

+ +

filledRectangle is used to draw a rectangle with the +two corners (upper left first, then lower right) specified, filled using +the color index specified.

+ + +

gdImage:polygon({ { x1, y1 }, { x2, y2 } ... }, color)

+ +

polygon is used to draw a polygon with the verticies +(at least 3) specified in the table, using the color index specified.

+ +

Example:

+
+im = assert(gd.createTrueColor(80, 80))
+black = im:colorAllocate(0, 0, 0)
+white = im:colorAllocate(255, 255, 255)
+im:polygon( { { 10, 10 }, { 10, 20 }, { 20, 20 }, { 20, 10 } }, white)
+im:png("out.png")
+
+ + + + +

gdImage:filledPolygon({ { x1, y1 }, { x2, y2 } ... }, color)

+ +

polygon is used to fill a polygon with the verticies +(at least 3) specified in the table, using the color index specified.

+ +

Example:

+
+im = assert(gd.createTrueColor(80, 80))
+black = im:colorAllocate(0, 0, 0)
+white = im:colorAllocate(255, 255, 255)
+im:filledPolygon( { { 30, 30 }, { 30, 40 }, { 40, 40 }, { 40, 30 } }, white)
+im:png("out.png")
+
+ + + + + +

gdImage:openPolygon({ { x1, y1 }, { x2, y2 } ... }, color)

+ +

openPolygon is used to draw a sequence of lines with the +verticies (at least 3) specified, using the color index specified. Unlike +polygon, the enpoints of the line sequence are not connected to +a closed polygon.

+ +

Example:

+
+im = assert(gd.createTrueColor(80, 80))
+black = im:colorAllocate(0, 0, 0)
+white = im:colorAllocate(255, 255, 255)
+im:openPolygon( { { 50, 50 }, { 50, 60 }, { 60, 60 }, { 60, 50 } }, white)
+im:png("out.png")
+
+ + + + +

gdImage:arc(cx, cy, w, h, s, e, color)

+ +

arc is used to draw a partial ellipse centered at the +given point, with the specified width and height in pixels. The arc +begins at the position in degrees specified by s and ends at the +position specified by e . The arc is drawn in the color specified +by the last argument. A circle can be drawn by beginning from 0 degrees +and ending at 360 degrees, with width and height being equal. e +must be greater than s. Values greater than 360 are interpreted +modulo 360.

+ + +

gdImage:filledArc(cx, cy, w, h, s, e, color, style)

+ +

filledArc is used to draw a partial ellipse centered at +the given point, with the specified width and height in pixels. The arc +begins at the position in degrees specified by s and ends at the +position specified by e. The arc is filled in the color specified +by color. A circle can be drawn by beginning from 0 degrees +and ending at 360 degrees, with width and height being equal. e +must be greater than s. Values greater than 360 are interpreted +modulo 360. The last argument is a sum of the following possibilities: +gd.ARC, gd.CHORD, gd.PIE +(synonym for gd.CHORD), gd.NO_FILL and +gd.EDGED. gd.ARC and gd.CHORD are +mutually exclusive; gd.CHORD just connects the starting +and ending angles with a straight line, while gd.ARC +produces a rounded edge. gd.PIE is a synonym for +gd.ARC. gd.NO_FILL indicates that the arc +or chord should be outlined, not filled. gd.EDGED, used +together with gd.NO_FILL, indicates that the beginning and +ending angles should be connected to the center; this is a good way to +outline (rather than fill) a 'pie slice'.

+ + +

gdImage:filledEllipse(cx, cy, w, h, color)

+ +

filledEllipse is used to draw an ellipse centered at the +given point, with the specified width and height in pixels. The ellipse +is filled in the color specified by the last argument.

+ + +

gdImage:fill(x, y, c)

+ +

fill floods a portion of the image with the specified +color, beginning at the specified point and flooding the surrounding +region of the same color as the starting point. For a way of flooding +a region defined by a specific border color rather than by its interior +color, see fillToBorder.

+ +

The fill color can be gd.TILED, resulting in a tile +fill using another image as the tile. However, the tile image cannot be +transparent. If the image you wish to fill with has a transparent color +index, call setTransparent on the tile image and set the +transparent color index to nil to turn off its transparency.

+ +

gdImage:fillToBorder(x, y, border_color, color)

+ +

fillToBorder floods a portion of the image with the +specified color, beginning at the specified point and stopping at the +specified border color. For a way of flooding an area defined by the +color of the starting point, see fill.

+ +

The border color cannot be a special color such as +gd.TILED; it must be a proper solid color. The fill color +can be, however.

+ + +

gdImage:saveAlpha(boolean)

+ +

By default, gd does not saves the alpha channel on a PNG image, an option +that saves some space in the output file. To enable this, just call up this +method passing true as the sole paremeter.

+ + +

gdImage:alphaBlending(boolean)

+ +

This method allows for two different modes of drawing on truecolor images. +In blending mode, the alpha channel component of the color supplied to all +drawing methods determines how much of the underlying color should be allowed +to shine through. As a result, Lua-GD automatically blends the existing color +at that point with the drawing color, and stores the result in the image, +resulting in a opaque pixel. In non-blending mode, the drawing color is +copied with its alpha channel information, replacing the destination pixel. +Blending mode is not available when drawing on palette images.

+ + +

Text drawing methods

+ +

gdImage:string(font, x, y, string, color)

+ +

This method draws the string in the fourth argument on the image +using one of the standard gd fonts (gd.FONT_TINY, +gd.FONT_SMALL, gd.FONT_MEDIUM, +gd.FONT_LARGE or gd.FONT_GIANT) from left +to right, starting from the (x, y) point and with the color specified +by the fifth argument. The string must have only ISO-8859-1 characters +(you should use gd.stringFT and True Type fonts for drawing +Unicode strings.

+ +

Example:

+
+im = gd.create(140, 80)
+white = im:colorAllocate(255, 255, 255)
+black = im:colorAllocate(0, 0, 0)
+im:string(gd.FONT_TINY, 10, 20, "TINY", black)
+im:string(gd.FONT_SMALL, 10, 30, "SMALL", black)
+im:string(gd.FONT_MEDIUM, 10, 45, "MEDIUM", black)
+im:string(gd.FONT_LARGE, 10, 58, "LARGE", black)
+im:string(gd.FONT_GIANT, 10, 75, "GIANT", black)
+im:png("out.png")
+
+ + +

gdImage:stringUp(fontname, x, y, string, color)

+ +

Similar to gd.string but the string will be drawn +vertically (rotated 90 degrees), from bottom to up, starting from the +(x, y) point. The string must have only ISO-8859-1 characters. If you +want use Unicode characters or other rotations than 90Β°, you should +use gd.stringFT and True Type fonts.

+ +

gdImage:stringFT(color, fontname, ptsize, angle, x, y, string)

+ +

stringFT draws a string of anti-aliased characters on the +image using the FreeType library to render user-supplied TrueType fonts. +The string is anti-aliased, meaning that there should be fewer "jaggies" +visible. The fontname is the full pathname to a TrueType font file, +or a font face name if the GDFONTPATH environment variable +have been set intelligently. In the absence of a full path, the font face +name may be presented with or without extension. Font names can also be +fontconfig +patterns (see gd.useFontConfig) so, the library will +automatically use fonts from your operating system.

+ +

The string argument is considered to be encoded via the UTF-8 +standard; also, HTML entities are supported, including decimal, +hexadecimal, and named entities. Those who are passing ordinary +ASCII strings may have difficulty with the & character unless +encoded correctly as &amp; but should have no other +difficulties.

+ +

The string may be arbitrarily scaled (ptsize) and rotated (angle in +radians). The direction of rotation is counter-clockwise, with 0 radians +(0 degrees) at 3 o'clock and PI/2 radians (90 degrees) at 12 o'clock +(you should use math.rad to convert degrees to radians). +The string is rendered in the color indicated by the color index. Use +the negative of the desired color index to disable anti-aliasing.

+ +

This method return eight values with the limits of the rendered +text on sucess or nil on failure. The following code show how to +use them to draw a bounding rectangle arround the text.

+ +
+im = gd.createTrueColor(100, 100)
+black = im:colorAllocate(0, 0, 0)
+white = im:colorAllocate(255, 255, 255)
+blue  = im:colorAllocate(0, 0, 240)
+im:filledRectangle(0, 0, 100, 100, black)
+llx, lly, lrx, lry, urx, ury, ulx, uly = im:stringFT(white, "Vera.ttf", 20, math.rad(45), 20, 90, "Lua-GD")
+im:polygon({ {llx, lly}, {lrx, lry}, {urx, ury}, {ulx, uly} }, blue)
+
+ +

Variables used above can be translated as follows. The points are relative +to the text regardless of the angle, so "upper left" means in the top +left-hand corner seeing the text horizontally.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
# Name Meaning
1 llx lower left corner, X position
2 lly lower left corner, Y position
3 lrx lower right corner, X position
4 lry lower right corner, Y position
5 urx upper right corner, X position
6 ury upper right corner, Y position
7 ulx upper left corner, X position
8 uly upper left corner, Y position
+ +

Also, there is a way to get these values before drawing on the image +passing nil as the first argument of gd.stringFT (note +the absence of the colon notation!), as follows. This is a relatively +cheap operation if followed by a rendering of the same string, because +of the caching of the partial rendering during bounding rectangle +calculation.

+ +
+llx, lly, lrx, lry, urx, ury, ulx, uly = gd.stringFT(nil, white, "Vera.ttf", 20, math.rad(45), 20, 90, "Lua-GD")
+
+ + + +

gdImage:stringFTEx(color, fontname, ptsize, angle, x, y, string, extra)

+ +

This is an extended version of stringFT that accepts +a table as an extra argument, which allows you to pass some +parameters to the rendering engine. The fields currently available are +show bellow:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field name Type Default Usage
linespacing float 1.05 Allows you to set the space between lines when rendering multiple + lines. A line spacing of 1.0 is the minimum to guarantee that lines + of text do not collide.
charmap indexed gd.FTEX_Unicode Allows you to set a specific character encoding schema, which can + be any of gd.FTEX_Unicode, + gd.FTEX_Shift_JIS, or gd.FTEX_Big5. It is + useful when a font offers more than one of Unicode, Shift_JIS, and + Big5. If you do not specify one, Unicode will be tried first. If + the preferred character mapping is not found in the font, other + character mappings are attempted.
hdpi integer 96 Sets the horizontal resolution passed to the FreeType engine.
vdpi integer 96 Sets the vertical resolution passed to the FreeType engine.
disable_kerning boolean false If fontconfig is available, gd will normally attempt to apply + kerning tables to adjust the relative positions of consecutive + characters more ideally for that pair of characters. This field + allows you to turn this feature off.
xshow boolean false Request gd to return a vector of individual character position + advances, occasionally useful in applications that must know exactly + where each character begins. This is returned as a string with values + separated by spaces.
return_font_path_name boolean false Returns the path to the font used to render the text. This is + useful because gd are capable of selecting a font automatically + based on a fontconfig font pattern when fontconfig is available.
fontconfig boolean false gd can use fontconfig to resolve font names, including fontconfig + patterns, if this field is set to true. As a convenience, + this behavior can also be made the default by calling + gd.useFontConfig with a true value.
+ +

This method also returns the same values than stringFT, +more two aditional ones if the fields xshow or +return_font_path_name are set to true. See the following +example:

+ +
+llX, llY, lrX, lrY, urX, urY, ulX, ulY = 
+  im:stringFTEx(blue, "Vera", 20, 0, 50, 50, "Lua-GD",
+    { hdpi = 40, vdpi = 20 } )
+
+llX, llY, lrX, lrY, urX, urY, ulX, ulY, pos = 
+  im:stringFTEx(blue, "Vera", 20, 0, 50, 150, "Lua-GD",
+    { hdpi = 40, vdpi = 20, xshow = true } )
+
+llX, llY, lrX, lrY, urX, urY, ulX, ulY, fontpath = 
+  im:stringFTEx(blue, "Vera", 20, 0, 50, 250, "Lua-GD",
+    { hdpi = 40, vdpi = 20, return_font_path_name = true } )
+
+llX, llY, lrX, lrY, urX, urY, ulX, ulY, pos, fontpath = 
+  im:stringFTEx(blue, "Vera", 20, 0, 50, 350, "Lua-GD",
+    { hdpi = 40, vdpi = 20, xshow = true, return_font_path_name = true } )
+
+ + + + +

gdImage:stringFTCircle(cx, cy, radius, textRadius, fillPortion, fontname, points, top, bottom, color)

+ +

Draws the text strings specified by top and bottom on +the image, curved along the edge of a circle of radius radius, +with its center at cx and cy. top is written +clockwise along the top; bottom is written counterclockwise along +the bottom. textRadius determines the "height" of each character; +if textRadius is 1/2 of radius, characters extend halfway +from the edge to the center. fillPortion varies from 0 to 1.0, +with useful values from about 0.4 to 0.9, and determines how much of the +180 degrees of arc assigned to each section of text is actually occupied +by text; 0.9 looks better than 1.0 which is rather crowded. fontname +is a freetype font; see gd.stringFT. points is +passed to the freetype engine and has an effect on hinting; although +the size of the text is determined by radius, textRadius, +and fillPortion, you should pass a point size that "hints" +appropriately -- if you know the text will be large, pass a large point +size such as 24.0 to get the best results. color can be any color, +and may have an alpha component, do blending, etc.

+ + +

Font configuration methods

+ +

gd.useFontConfig(bool)

+

gd has the ability to use +fontconfig +patterns rather than font file names as parameters to +gd.stringFT and gd.stringFTCircle. For +backwards compatibility reasons, the fontlist parameter to those +functions is still expected to be a full or partial font file path +name or list thereof by default. However, as a convenience, a single +call to gd.useFontConfig with a true parameter +configures Lua-GD to expect the fontlist parameter to be a fontconfig +pattern. Otherwise, calling gd.useFontConfig with a +false parameter will disable the fontconfig patters.

+ + +

gd.fontCacheSetup()

+ +

This function initializes the font cache for freetype text +output functions such as gd.stringFT. If this function +is not called by the programmer, it is invoked automatically on the +first truetype text output call, which is perfectly safe unless the +application is multithreaded (either using LuaThreads or running Lua from +a different thread in your host program which also uses gd from the C API) +. Multithreaded applications should directly invoke this function before +allowing any thread to use freetype text output. You don't need to call +this functions if you are using Lua coroutines because any calls from +Lua to C API ara atomic. This function returns true on success +or false if the freetype library fails to initialize.

+ + +

gd.fontCacheShutdown()

+ +

This function releases the memory used by the freetype font cache +and the text output mutex. Applications that use Lua-GD for their entire +lifetime, then exit, need not call this function. This function has no +return value.

+ + + +

Image copying, resizing and transformation methods

+ + +

gd.copy(dstImage, srcImage, dstX, dstY, srcX, srcY, w, h)
+dstImage:copy(srcImage, dstX, dstY, srcX, srcY, w, h)

+ +

gd.copy is used to copy a rectangular portion of one +image to another image (For a way of stretching or shrinking the image +in the process, see gd.copyResized).

+ +

The dstImage argument is the destination image to which +the region will be copied (you can use the colon notation for it). The +srcImage argument is the source image from which the region is +copied. The dstX and dstY arguments specify the point in +the destination image to which the region will be copied. The srcX +and srcY arguments specify the upper left corner of the region +in the source image. The w and h arguments specify the +width and height of the region.

+ +

When you copy a region from one location in an image to another +location in the same image, gd.copy will perform as +expected unless the regions overlap, in which case the result is +unpredictable.

+ +

Important note on copying between images: since different +images do not necessarily have the same color tables, pixels are +not simply set to the same color index values to copy them. If the +destination image is a palette image, this method will use the +gd.colorResolve method to determine the best color +available.

+ + + +

gd.copyResized(dstImage, srcImage, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH)
+dstImage:copyResized(srcImage, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH)

+ + +

gd.copyResized is used to copy a rectangular portion of +one image to another image. The X and Y dimensions of the original region +and the destination region may vary, resulting in stretching or shrinking +of the region as appropriate (For a simpler version of this method +which does not deal with resizing, see gd.copy).

+ +

The dstImage argument is the destination image to which the +region will be copied. The srcImage argument is the source +image from which the region is copied. The dstX and dstY +arguments specify the point in the destination image to which the region +will be copied. The srcX and srcY arguments specify the +upper left corner of the region in the source image. The dstW +and dstH arguments specify the width and height of the destination +region. The srcW and srcH arguments specify the width and +height of the source region and can differ from the destination size, +allowing a region to be scaled during the copying process.

+ +

When you copy a region from one location in an image to another +location in the same image, gd.copyResized will perform +as expected unless the regions overlap, in which case the result is +unpredictable.

+ +

Important note on copying between images: since different +images do not necessarily have the same color tables, pixels are +not simply set to the same color index values to copy them. If the +destination image is a palette image, this method will use the +gd.colorResolve method to determine the best color +available.

+ + + + +

gd.copyResampled(dstImage, srcImage, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH)
+dstImage:copyResampled(srcImage, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH)

+ +

gd.copyResampled is used to copy a rectangular portion +of one image to another image, smoothly interpolating pixel values so +that, in particular, reducing the size of an image still retains a great +deal of clarity. The X and Y dimensions of the original region and the +destination region can vary, resulting in stretching or shrinking of the +region as appropriate. (For a simpler version of this method which does +not deal with resizing, see gd.copy. For a version which +does not interpolate pixel values, see gd.copyResized).

+ +

Pixel values are only interpolated if the destination image is a +truecolor image. Otherwise, gd.copyResized is automatically +invoked.

+ +

The dstImage argument is the destination image to which +the region will be copied. The srcImage argument is the source +image from which the region is copied. The dstX and dstY +arguments specify the point in the destination image to which the region +will be copied. The srcX and srcY arguments specify the +upper left corner of the region in the source image. The dstW +and dstH arguments specify the width and height of the destination +region. The srcW and srcH arguments specify the width and +height of the source region and can differ from the destination size, +allowing a region to be scaled during the copying process.

+ +

When you copy a region from one location in an image to another +location in the same image, gd.copyResampled will perform +as expected unless the regions overlap, in which case the result is +unpredictable. If this presents a problem, create a scratch image +in which to keep intermediate results.

+ +

Important note on copying between images: since different +images do not necessarily have the same color tables, pixels are +not simply set to the same color index values to copy them. If the +destination image is a palette image, this method will use the +gd.colorResolve method to determine the best color +available.

+ + + + +

gd.copyRotated(dstImage, srcImage, dstX, dstY, srcX, srcY, srcW, srcH, ang)
+dstImage:copyRotated(srcImage, dstX, dstY, srcX, srcY, srcW, srcH, ang)

+ +

gd.copyRotated is used to copy a rectangular portion of +one image to another image, or to another region of the same image. The +srcX and srcY coordinates specify the upper left corner of +the source area; however, the dstX and dstY coordinates +specify the center of the destination area. This important +distinction is made because the rotated rectangle may may or may not be +parallel to the X and Y axes. The destination coordinates may be floating +point, as the center of the desired destination area may lie at the +center of a pixel (0.5 pixels) rather than its upper left corner. The +angle specified is an integer number of degrees, between 0 and 360, +with 0 degrees causing no change, and counterclockwise rotation as the +angle increases.

+ +

When you copy a region from one location in an image to another +location in the same image, gd.copyRotated will perform +as expected unless the regions overlap, in which case the result is +unpredictable. If this presents a problem, create a scratch image +in which to keep intermediate results.

+ +

Important note on copying between images: since different +images do not necessarily have the same color tables, pixels are +not simply set to the same color index values to copy them. If the +destination image is a palette image, this method will use the +gd.colorRotated method to determine the best color +available.

+ + + +

gd.copyMerge(dstImage, srcImage, dstX, dstY, srcX, srcY, w, h, pct)
+dstImage:copyMerge(srcImage, dstX, dstY, srcX, srcY, w, h, pct)

+ +

gd.copyMerge is almost identical to gd.copy, +except that it 'merges' the two images by an amount specified in the +last parameter. If the last parameter is 100, then it will method +identically to gd.copy - the source image replaces the +pixels in the destination.

+ +

If, however, the pct parameter is less than 100, then the two images +are merged. With pct = 0, no action is taken.

+ +

This feature is most useful to 'highlight' sections of an image by +merging a solid color with pct = 50.

+ + + +

gd.copyMergeGray(dstImage, srcImage, dstX, dstY, srcX, srcY, w, h, pct)
+dstImage:copyMergeGray(srcImage, dstX, dstY, srcX, srcY, w, h, pct)

+ +

gd.copyMergeGray is almost identical to +gd.copyMerge, except that when merging images it preserves +the hue of the source by converting the destination pixels to grey scale +before the copy operation.

+ + + +

gd.paletteCopy(dstImage, srcImage)
+dstImage:paletteCopy(srcImage)

+ +

gd.paletteCopy copies a palette from one image to another, +attempting to match the colors in the target image to the colors in the +source palette.

+ + + +

gdImage:squareToCircle(radius)

+ +

Returns a new image of width and height radius * 2, in which the X +axis of the original has been remapped to theta (angle) and the +Y axis of the original has been remapped to rho (distance from +center). This is known as a "polar coordinate transform". The image +must be square, but can have any size.

+ + + +

gdImage:sharpen(pct)

+ +

Sharpens the specified image. pct is a sharpening percentage, +and can be greater than 100. Silently does nothing to non-truecolor +images. Silently does nothing for pct < 0. Transparency/alpha +channel are not altered.

+ + + +

gdImage:trueColorToPalette(dither, colorsWanted)

+ +

This method converts a truecolor image to a palette-based image, +using a high-quality two-pass quantization routine. If dither +is set to true, the image will be dithered to approximate colors +better, at the expense of some obvious "speckling". colorsWanted, +an integer value, can be anything up to 256. If the original source image +includes photographic information or anything that came out of a JPEG, +256 is strongly recommended. 100% transparency of a single transparent +color in the original truecolor image will be preserved. There is no +other support for preservation of alpha channel or transparency in the +destination image.

+ +

For best results, don't use this method -- write real truecolor +PNGs and JPEGs. The disk space gain of conversion to palette is not great +(for small images it can be negative) and the quality loss is ugly.

+ + + + +

GIF animation methods

+ +

gdImage:gifAnimBegin(filename, globalCM, loops)

+ +

This method must be called as the first method when creating a GIF +animation in a file on the disk. It writes the correct GIF file headers +to file name filename and prepares for frames to be added for +the animation. The image argument is not used to produce an image frame +to the file, it is only used to establish the GIF animation frame size, +interlacing options and the color palette. gd.gifAnimAdd +is used to add the first and subsequent frames to the animation, and +the animation must be terminated by calling gd.gifAnimEnd, +passing the same file name.

+ +

The globalCM flag indicates if a global color map (or palette) +is used in the GIF89A header. A true value specifies that a +global color map should be used to reduce the size of the animation. Of +course, if the color maps of individual frames differ greatly, a global +color map may not be a good idea, so you should use globalCM = +false.

+ +

If loops is 0 or greater, the Netscape 2.0 extension for +animation loop count is written. 0 means infinite loop count. -1 means +that the extension is not added which results in no looping. -1 is +the default.

+ + + +

gdImage:gifAnimBeginStr(globalCM, loops)

+ +

This method must be called as the first method when creating a GIF +animation in memory. It returns the correct GIF file headers as an Lua +string and prepares for frames to be added for the animation. The image +argument is not used to produce an image frame to the file, it is only +used to establish the GIF animation frame size, interlacing options +and the color palette. New frames must be created with the method +gd.gifAnimAddStr and concatened to this one. Finally, +one last frame, generated with the method gd.gifAnimEndStr, +must be concatened to finish the animation.

+ +

The globalCM flag indicates if a global color map (or palette) +is used in the GIF89A header. A true value specifies that a +global color map should be used to reduce the size of the animation. Of +course, if the color maps of individual frames differ greatly, a global +color map may not be a good idea, so you should use globalCM = +false.

+ +

If loops is 0 or greater, the Netscape 2.0 extension for +animation loop count is written. 0 means infinite loop count. -1 means +that the extension is not added which results in no looping. -1 is +the default.

+ + + +

gdImage:gifAnimAdd(filename, localCM, leftOfs, topOfs, delay, disposal [, previm])

+ +

This method writes GIF animation frames to the file specified by +filename, which was created with gd.gifAnimBegin. With +leftOfs and topOfs you can place this frame in +different offset than (0, 0) inside the image screen as defined +in gd.gifAnimBegin. Delay between the previous +frame and this frame is in 1/100s units. Disposal is usually +gd.DISPOSAL_NONE, meaning that the pixels changed by this +frame should remain on the display when the next frame begins to render, +but can also be gd.DISPOSAL_UNKNOWN (not recommended), +gd.DISPOSAL_RESTORE_BACKGROUND (restores the first allocated +color of the global palette), or gd.DISPOSAL_RESTORE_PREVIOUS +(restores the appearance of the affected area before the frame was +rendered). Only gd.DISPOSAL_NONE is a sensible choice for the +first frame. If previm is passed, the built-in GIF optimizer will +always use gd.DISPOSAL_NONE regardless of the disposal +parameter.

+ +

Setting the localCM flag to true adds a local +palette for this image to the animation. Otherwise the global palette +is assumed and the user must make sure the palettes match. Use +gd.paletteCopy to do that.

+ +

Automatic optimization is activated by giving the previous +image as a parameter. This method then compares the images and +only writes the changed pixels to the new frame in animation. The +disposal parameter for optimized animations must be set to +gd.DISPOSAL_NONE, also for the first frame. leftOfs +and topOfs parameters are ignored for optimized frames. To achieve +good optimization, it is usually best to use a single global color map. To +allow gd.gifAnimAdd to compress unchanged pixels via the use +of a transparent color, the image must include a transparent color.

+ + + + +

gdImage:gifAnimAddStr(localCM, leftOfs, topOfs, delay, disposal [, previm])

+ +

This method returns GIF animation frames as Lua +strings to be concatenated to headers which was created with +gd.gifAnimBeginStr. With leftOfs and topOfs +you can place this frame in different offset than (0, 0) inside the image +screen as defined in gd.gifAnimBegin. Delay between the +previous frame and this frame is in 1/100s units. Disposal is usually +gd.DISPOSAL_NONE, meaning that the pixels changed by this +frame should remain on the display when the next frame begins to render, +but can also be gd.DISPOSAL_UNKNOWN (not recommended), +gd.DISPOSAL_RESTORE_BACKGROUND (restores the first allocated +color of the global palette), or gd.DISPOSAL_RESTORE_PREVIOUS +(restores the appearance of the affected area before the frame was +rendered). Only gd.DISPOSAL_NONE is a sensible choice for the +first frame. If previm is passed, the built-in GIF optimizer will +always use gd.DISPOSAL_NONE regardless of the disposal +parameter.

+ +

Setting the localCM flag to true adds a local +palette for this image to the animation. Otherwise the global palette +is assumed and the user must make sure the palettes match. Use +gd.paletteCopy to do that.

+ +

Automatic optimization is activated by giving the previous +image as a parameter. This method then compares the images and +only writes the changed pixels to the new frame in animation. The +disposal parameter for optimized animations must be set to +gd.DISPOSAL_NONE, also for the first frame. leftOfs +and topOfs parameters are ignored for optimized frames. To achieve +good optimization, it is usually best to use a single global color map. To +allow gd.gifAnimAdd to compress unchanged pixels via the use +of a transparent color, the image must include a transparent color.

+ + +

gdImage:gifAnimEnd(filename)

+ +

Finishes a GIF animation properly by writing a semicolon character +(;) to the output file. You must call this method (or write the semicolon +by yourself) to create valid animations.

+ + + +

gdImage:gifAnimEndStr()

+ +

Returns a semicolon character (;) as a Lua string, which must +be concatened as the last element of an animation created with +gd.gifAnimBeginStr and gd.gifAnimAddStr. Of +course, you can concatenate a semicolon by yourself, but this is a more +explicit way to finish the animation.

+ + + + +

Examples

+ + +

CGI Web Counter

+
+#!/usr/bin/env lua
+-- counter.lua -- a web counter in Lua!
+-- (c) 2004 Alexandre Erwin Ittner
+
+local gc = require("gd")
+
+datafile = "counter.txt"
+fp = io.open(datafile, "r+")
+if fp then
+  cnt = tonumber(fp:read("*l")) or 0
+  fp:seek("set", 0)
+else
+  cnt = 0
+  fp = io.open(datafile, "w")
+  assert(fp)
+end
+cnt = cnt + 1
+fp:write(cnt .."\n")
+fp:close()
+
+sx = math.max(string.len(tostring(cnt)), 1) * 8
+im = gd.create(sx, 15)
+-- first allocated color defines the background.
+white = im:colorAllocate(255, 255, 255)
+im:colorTransparent(white)
+black = im:colorAllocate(0, 0, 0)
+im:string(gd.FONT_MEDIUM, 1, 1, cnt, black)
+
+print("Content-type: image/png\n")
+io.write(im:pngStr())
+
+ + +

CGI Analog clock

+ +

Clock Example output

+ +
+#!/usr/bin/env lua
+
+-- a cgi script that draws an analog clock with lua and lua-gd
+-- (c) 2004 Alexandre Erwin Ittner
+
+local gd = require("gd")
+
+function createClock(size, hours, minutes)
+  local im = gd.createTrueColor(size, size)
+  local white = im:colorAllocate(255, 255, 255)
+  local gray = im:colorAllocate(128, 128, 128)
+  local black = im:colorAllocate(0, 0, 0)
+  local blue = im:colorAllocate(0, 0, 128)
+  local cxy = size/2
+
+  im:filledRectangle(0, 0, size, size, white)
+  im:setThickness(math.max(1, size/100))
+  im:arc(cxy, cxy, size, size, 0, 360, black)
+
+  local ang = 0
+  local rang, gsize
+  while ang < 360 do
+    rang = math.rad(ang)
+    if (ang % 90) == 0 then
+      gsize = 0.75
+    else
+      gsize = 0.85
+    end
+    im:line(
+      cxy + gsize * cxy * math.sin(rang),
+      size - (cxy + gsize * cxy * math.cos(rang)),
+      cxy + cxy * 0.9 * math.sin(rang),
+      size - (cxy + cxy * 0.9 * math.cos(rang)),
+      gray)
+    ang = ang + 30
+  end
+
+  im:setThickness(math.max(1, size/50))
+  im:line(cxy, cxy,
+    cxy + 0.45 * size * math.sin(math.rad(6*minutes)),
+    size - (cxy + 0.45 * size * math.cos(math.rad(6*minutes))),
+    blue)
+
+  im:setThickness(math.max(1, size/25))
+  rang = math.rad(30*hours + minutes/2)
+  im:line(cxy, cxy, 
+    cxy + 0.25 * size * math.sin(rang),
+    size - (cxy + 0.25 * size * math.cos(rang)),
+    blue)
+
+  im:setThickness(1)
+  local sp = math.max(1, size/20)
+  im:filledArc(cxy, cxy, sp, sp, 0, 360, black, gd.ARC)
+
+  return im
+end
+
+dh = os.date("*t")
+im = createClock(100, dh.hour, dh.min)
+
+print("Content-type: image/png")
+print("Refresh: 60")            -- Ask browser to reload the image after 60s
+print("Pragma: no-cache")       -- Can mozilla understand this?
+print("Expires: Thu Jan 01 00:00:00 UTC 1970")  -- Marks as expired
+print("")
+
+io.write(im:pngStr())
+
+ + +

The Sierpinski triangle

+ +

The Sierpinski triangle

+ +
+#!/usr/bin/env lua
+-- Draws the famous Sierpinski triangle with lua-gd
+
+local gd = require("gd")
+
+size = 250
+im = gd.createPalette(size, size)
+white = im:colorAllocate(255, 255, 255)
+black = im:colorAllocate(0, 0, 0)
+
+m = {}
+m[math.floor(size/2)] = true
+
+for i = 1, size do
+  n = {}
+  for j = 1, size do
+    if m[j] then
+      im:setPixel(j, i, black)
+      n[j+1] = not n[j+1]
+      n[j-1] = not n[j-1]
+    end
+  end
+  m = n
+end
+
+im:png("sierpinski.png")
+
+ + + + +

Using Freetype, Fontconfig and TrueType fonts

+ +

Fontconfig Example output

+ +
+-- The fonts used in this example comes with Microsoft operating systems 
+-- and can be downloaded from http://corefonts.sourceforge.net
+
+local gd = require("gd")
+
+im = gd.createTrueColor(220, 190)
+white = im:colorAllocate(255, 255, 255)
+black = im:colorAllocate(0, 0, 0)
+x, y = im:sizeXY()
+im:filledRectangle(0, 0, x, y, white)
+
+gd.useFontConfig(true)
+im:stringFT(black, "Arial", 20, 0, 10, 30, "Standard Arial")
+im:stringFT(black, "Arial:bold", 20, 0, 10, 60, "Bold Arial")
+im:stringFT(black, "Arial:italic", 20, 0, 10, 90, "Italic Arial")
+im:stringFT(black, "Arial:bold:italic", 20, 0, 10, 120, "Italic Bold Arial")
+im:stringFT(black, "Times New Roman", 20, 0, 10, 150, "Times New Roman")
+im:stringFT(black, "Comic Sans MS", 20, 0, 10, 180, "Comic Sans MS")
+
+im:png("out.png")
+
+ + + +

GIF animation

+ +

A GIF animation

+ +
+local gd = require("gd")
+
+im = gd.createPalette(120, 120)
+assert(im)
+
+black = im:colorAllocate(0, 0, 0)
+blue = {}
+for i = 1, 20 do
+  blue[i] = im:colorAllocate(0, 0, 120+6*i)
+end
+
+fp = io.open("out.gif", "w")
+assert(fp, "Failed to open file for writting")
+
+fp:write(im:gifAnimBeginStr(true, 0))
+
+for i = 1, 20 do
+  tim = gd.createPalette(120, 120)
+  tim:paletteCopy(im)
+  tim:arc(60, 60, 6*i, 6*i, 0, 360, blue[21-i])
+  fp:write(tim:gifAnimAddStr(false, 0, 0, 5, gd.DISPOSAL_NONE))
+end
+
+fp:write(gd.gifAnimEndStr())
+fp:close()
+
+ + + +

A Steganography Application

+ +

+ Original test image. + Image with a staganographic message + Resulting image from 'steg.lua diff' +
+ Left: Original test image. Center: Image with a steganographic message (no + visible changes); Right: resulting image from "steg.lua diff" + showing, in color, the pixels changed to store the message. +

+ + +
+#/usr/bin/env lua
+--[[
+                      Steganography with Lua-GD
+
+Steganography is the technique of writing hidden messages in such a way
+that no one apart from the intended recipient knows of the existence of
+the message; this is in contrast to cryptography, where the existence
+of the message is clear, but the meaning is obscured. Generally a
+steganographic message will appear to be something else, like a shopping
+list, an article, a picture, or some other "cover" message. In the
+digital age, steganography works by replacing bits of useless or unused
+data in regular computer files (such as graphics, sound, text, HTML, or
+even floppy disks) with bits of different, invisible information. This
+hidden information can be plain text, cipher text or even images.
+
+
+                           A Simple Example
+
+If Alice wants to send a secret message to Bob through an insecure
+channel, she can use some encryption software (like GnuPG) to encrypt
+the message with Bob's public key. It's a good solution because no
+one unless Bob will be able to read the message. She can also sign the
+message so Bob will know that the message really comes from her. BUT,
+a potential attacker will know that a ciphered message was sent. If the
+attacker has control over the communication channel, he might block the
+message in some way that Bob will never receive it. If Alice also HIDES
+the ciphertext in an unsuspected piece of information (like a photo of her
+cat) the attacker will not detect it and the message will arrive to Bob.
+
+This program will help Alice to hide some arbitrary text in a PNG image by
+replacing the least significant bits of each color channel of some pixels
+with bits from the encrypted message. PNG or other loseless compression
+algorithm are mandatory here, since compressing the image with a lossy
+algorithm will destroy the stored information. The maximum length of the
+message is limited by the image's size (each byte needs 8 color channels or
+2 pixels and 2 channels from the next pixel). So, the image must have at
+least "ceil((length+1)*8/3)" pixels (the extra byte is the NUL marker for
+the end of the string). So, if Alice's message is "Meet me in the secret
+place at nine o'clock.", she will encrypt and sign it to something like
+"PyJYDpz5LCOSHPiXDvLHmVzxLV8qS7EFvZnoo1Mxk+BlT+7lMjpQKs" (imagine Alice's
+cat walking in you keyboard :). This is the ciphertext that will be sent
+to Bob through the image.
+
+The following table shows what happens to the first eight pixels from
+the image when mixed to the first three bytes from the encrypted message:
+
+
+         +-----+---+----------+-----------------+----------+
+         | Pix | C | Orig img |     Message     | New img  |
+         |  #  |   |   bits   | Chr | Dec | Bin |   bits   |
+         +-----+---+----------+-----+-----+-----+----------+
+         |     | R | 01010010 |     |     |  0  | 01010010 |
+         |  1  | G | 00101010 |     |     |  1  | 00101011 |
+         |_____| B | 00010101 |     |     |  0  | 00010100 |
+         |     | R | 11100100 |  P  | 080 |  1  | 11100101 |
+         |  2  | G | 00100100 |     |     |  0  | 00100100 |
+         |_____| B | 01001111 |     |     |  0  | 01001110 |
+         |     | R | 01010010 |     |     |  0  | 01010010 |
+         |  3  | G | 00101110 |_____|_____|__0__| 00101110 |
+         |_____| B | 00111001 |     |     |  0  | 00111000 |
+         |     | R | 10010110 |     |     |  1  | 10010111 |
+         |  4  | G | 01011101 |     |     |  1  | 01011101 |
+         |_____| B | 00100101 |  y  | 121 |  1  | 00100101 |
+         |     | R | 01001001 |     |     |  1  | 01001001 |
+         |  5  | G | 10110110 |     |     |  0  | 10110110 |
+         |_____| B | 00010101 |     |     |  0  | 00010100 |
+         |     | R | 00110100 |_____|_____|__1__| 00110101 |
+         |  6  | G | 01000111 |     |     |  0  | 01000110 |
+         |_____| B | 01001000 |     |     |  1  | 01001001 |
+         |     | R | 01010110 |     |     |  0  | 01010110 |
+         |  7  | G | 00011001 |     |     |  0  | 00011000 |
+         |_____| B | 10010100 |  J  | 074 |  1  | 10010101 |
+         |     | R | 00010101 |     |     |  0  | 00010100 |
+         |  8  | G | 01011010 |     |     |  1  | 01011011 |
+         |     | B | 01010001 |     |     |  0  | 01010000 |
+         +-----+---+----------+-----+-----+-----+----------+
+
+
+When Bob wants to read the message he will extract the least significant
+bit (LSB) from each color channel from some pixels of the image and
+join them to get the original ciphertext. A NULL character (ASCII #0)
+will mark the end of the message within the image, so he will know when
+to stop. Of course, this program will also do this boring job for Bob.
+
+--]]
+
+local gd = require("gd")
+
+function getLSB(n)
+  return (n % 2) ~= 0
+end
+
+
+-- Bizarre way to do some bit-level operations without bitlib.
+function setLSB(n, b)
+  if type(b) == "number" then
+    if b == 0 then
+      b = false
+    else
+      b = true
+    end
+  end
+  if getLSB(n) then
+    if b then
+      return n
+    elseif n > 0 then
+      return n - 1
+    else
+      return n + 1
+    end
+  else
+    if not b then
+      return n
+    elseif n > 0 then
+      return n - 1
+    else
+      return n + 1
+    end
+  end
+end
+
+
+function intToBitArray(n)
+  local ret = {}
+  local i = 0
+  while n ~= 0 do
+    ret[i] = getLSB(n)
+    n = math.floor(n/2)
+    ret.size = i
+    i = i + 1
+  end 
+  return ret
+end
+
+
+function printBitArray(a)
+  local i
+  for i = a.size,0,-1 do
+    if a[i] then
+      io.write("1")
+    else    
+      io.write("0")
+    end
+  end
+end
+
+
+function mergeMessage(im, msg)
+  local w, h = im:sizeXY()
+  msg = msg .. string.char(0)
+  local len = string.len(msg)
+  if h * w < len * 8 then
+    return nil
+  end
+  local x, y = 0, 0
+  local oim = gd.createTrueColor(w, h)
+  local i = 1
+  local a2, c, nc, chr
+  local a = {}
+  local s, e = 1, 1
+  local rgb = {}
+
+  while y < h do
+    c = im:getPixel(x, y)
+    rgb.r = im:red(c)
+    rgb.g = im:green(c)
+    rgb.b = im:blue(c)
+    if i <= len and  e - s < 3 then
+      a2 = intToBitArray(string.byte(string.sub(msg, i, i)))
+      for cnt = 7,0,-1 do
+        a[e+7-cnt] = a2[cnt]
+      end
+      i = i + 1
+      e = e + 8
+    end
+    if e - s > 0 then
+      rgb.r = setLSB(rgb.r, a[s])
+      a[s] = nil
+      s = s + 1
+    end
+    if e - s > 0 then
+      rgb.g = setLSB(rgb.g, a[s])
+      a[s] = nil
+      s = s + 1
+    end
+    if e - s > 0 then
+      rgb.b = setLSB(rgb.b, a[s])
+      a[s] = nil
+      s = s + 1
+    end
+    nc = oim:colorResolve(rgb.r, rgb.g, rgb.b)
+    oim:setPixel(x, y, nc)
+    x = x + 1
+    if x == w then
+      x = 0
+      y = y + 1
+    end
+  end
+  return oim, len*8, w*h
+end
+
+
+function getMessage(im)
+  local msg = {}
+  local w, h = im:sizeXY()
+  local x, y = 0, 0
+  local a = {}
+  local s, e = 1, 1
+  local b = 0
+  local c
+  while y <= h do
+    c = im:getPixel(x, y)
+    a[e] = getLSB(im:red(c))
+    a[e+1] = getLSB(im:green(c))
+    a[e+2] = getLSB(im:blue(c))
+    e = e + 2
+    if e - s >= 7 then
+      b = 0
+      for p = s, s+7 do
+        b = b * 2
+        if a[p] then
+          b = b + 1
+        end
+        a[p] = nil
+      end
+      s = s + 8
+      if b == 0 then
+        return table.concat(msg)
+      else
+        msg[#msg+1] = string.char(b)
+      end
+    end
+    e = e + 1
+    x = x + 1
+    if x == w then
+      x = 0
+      y = y + 1
+    end
+  end
+  return table.concat(msg)
+end
+
+
+function compare(fimg1, fimg2)
+  local im1 = gd.createFromPng(fimg1)
+  if not im1 then
+    print("ERROR: " .. fimg1 .. " bad PNG data.")
+    os.exit(1)
+  end
+  local im2 = gd.createFromPng(fimg2)
+  if not im2 then
+    print("ERROR: " .. fimg2 .. " bad PNG data.")
+    os.exit(1)
+  end
+  local w1, h1 = im1:sizeXY()
+  local w2, h2 = im2:sizeXY()
+  if w1 ~= w2 or h1 ~= h2 then
+    print("ERROR: Images have different sizes.")
+    os.exit(1)
+  end
+  local oim = gd.createTrueColor(w1, h1)
+  local x, y = 0, 0
+  local c1, c2, oc, f, fc
+  while y < h1 do
+    c1 = im1:getPixel(x, y)
+    c2 = im2:getPixel(x, y)
+    if im1:red(c1) ~= im2:red(c2)
+    or im1:green(c1) ~= im2:green(c2)
+    or im1:blue(c1) ~= im2:blue(c2) then
+      oc = oim:colorResolve(im2:red(c2), im2:green(c2), im2:blue(c2))
+      oim:setPixel(x, y, oc)
+    else
+      f = math.floor((im1:red(c1) + im1:green(c1) + im1:blue(c1))/6.0)
+      fc = oim:colorResolve(f, f, f)
+      oim:setPixel(x, y, fc)
+    end
+    x = x + 1
+    if x == w1 then
+      x = 0
+      y = y + 1
+    end
+  end
+  return oim
+end
+    
+
+function usage()
+  print("Usage:")
+  print(" lua steg.lua hide <input file> <output file>")
+  print(" lua steg.lua show <input file>")
+  print(" lua steg.lua diff <input file 1> <input file 2> <output file>")
+  print("")
+  print(" hide - Reads a message from stdin and saves into <output file>.")
+  print(" show - Reads a message from <input file> and prints it to stdout.")
+  print(" diff - Compares two images and writes the diff to <output file>.")
+  print("")
+  print(" WARNING: All files used here must be in the PNG format!")
+end
+
+
+if not arg[1] or not arg[2] then
+  usage()
+  os.exit(1)
+end
+
+if arg[1] == "show" then
+  im = gd.createFromPng(arg[2])
+  if not im then    
+    print("ERROR: Bad image data.")
+    os.exit(1)
+  end
+  io.write(getMessage(im))
+  os.exit(0)
+end
+
+if arg[1] == "hide" then
+  if not arg[3] then
+    usage()
+    os.exit(1)
+  end
+  im = gd.createFromPng(arg[2])
+  if not im then
+    print("ERROR: Bad image data.")
+    os.exit(1)
+  end
+  print("Type your message and press CTRL+D to finish.")
+  msg = io.read("*a")
+  oim, l, t = mergeMessage(im, msg)
+  if not oim then
+    print("ERROR: Image is too small for the message.")
+    os.exit(1)
+  end
+  if not oim:png(arg[3]) then
+    print("ERROR: Failed to write output file.")
+    os.exit(1)
+  end
+  print(string.format("DONE: %2.1f%% of the image used to store the message.",
+    100.0*l/t))
+  os.exit(0)
+end
+
+if arg[1] == "diff" then
+  if not arg[3] and arg[4] then
+    usage()
+    os.exit(1)
+  end
+  oim = compare(arg[2], arg[3])
+  if not oim:png(arg[4]) then
+    print("ERROR: Failed to write output file.")
+    os.exit(1)
+  end
+  os.exit(0)
+end
+
+usage()
+os.exit(1)
+
+ + + +

Other examples

+

There are some useful examples in the demos directory +within the distribution package.

+ + + + +

Contact information

+ +

Author: Alexandre Erwin Ittner
+Email: alexandre#ittner.com.br +(email obfuscated to avoid spam-bots. Please replace the "#" with an "@"). + +
GnuPG/PGP Key: 0x0041A1FB + (key fingerprint: 9B49 FCE2 E6B9 D1AD 6101 29AD 4F6D F114 0041 A1FB). +
Homepage: http://www.ittner.com.br/. +
Location: JaraguΓ‘ do Sul, Santa Catarina, Brazil. +

+ + +
+ + + + + diff --git a/doc/lua-gd.png b/doc/lua-gd.png new file mode 100644 index 000000000..66412c8d4 Binary files /dev/null and b/doc/lua-gd.png differ diff --git a/doc/sierpinski.png b/doc/sierpinski.png new file mode 100644 index 000000000..30bb3f5f2 Binary files /dev/null and b/doc/sierpinski.png differ diff --git a/doc/stdfonts.png b/doc/stdfonts.png new file mode 100644 index 000000000..9f068bc04 Binary files /dev/null and b/doc/stdfonts.png differ diff --git a/lua-gd.ps b/lua-gd.ps new file mode 100644 index 000000000..bce203379 --- /dev/null +++ b/lua-gd.ps @@ -0,0 +1,210 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%Title: Lua logo +%%Creator: lua@tecgraf.puc-rio.br +%%CreationDate: Wed Nov 29 19:04:04 EDT 2000 +%%BoundingBox: -45 0 1035 1080 +%%Pages: 1 +%%EndComments +%%EndProlog + +%------------------------------------------------------------------------------ +% +% Graphic design by Alexandre Nakonechnyj. +% PostScript programming by the Lua team. +% This code is hereby placed in the public domain. +% +% Permission is hereby granted, without written agreement and without license +% or royalty fees, to use, copy, and distribute this logo for any purpose, +% including commercial applications, subject to the following conditions: +% +% * The origin of this logo must not be misrepresented; you must not +% claim that you drew the original logo. We recommend that you give credit +% to the graphics designer in all printed matter that includes the logo. +% +% * The only modification you can make is to adapt the orbiting text to +% your product name. +% +% * The logo can be used in any scale as long as the relative proportions +% of its elements are maintained. +% +%------------------------------------------------------------------------------ + +/LABEL (Lua-GD) def + +%-- DO NOT CHANGE ANYTHING BELOW THIS LINE ------------------------------------ + +/PLANETCOLOR {0 0 0.5 setrgbcolor} bind def +/HOLECOLOR {1.0 setgray} bind def +/ORBITCOLOR {0.5 setgray} bind def +/LOGOFONT {/Helvetica 0.90} def +/LABELFONT {/Helvetica 0.36} def + +%------------------------------------------------------------------------------ + +/MOONCOLOR {PLANETCOLOR} bind def +/LOGOCOLOR {HOLECOLOR} bind def +/LABELCOLOR {ORBITCOLOR} bind def + +/LABELANGLE 125 def +/LOGO (Lua) def + +/DASHANGLE 10 def +/HALFDASHANGLE DASHANGLE 2 div def + +% moon radius. planet radius is 1. +/r 1 2 sqrt 2 div sub def + +/D {0 360 arc fill} bind def +/F {exch findfont exch scalefont setfont} bind def + +% place it nicely on the paper +/RESOLUTION 1024 def +RESOLUTION 2 div dup translate +RESOLUTION 2 div 2 sqrt div dup scale + +%-------------------------------------------------------------------- planet -- +PLANETCOLOR +0 0 1 D + +%---------------------------------------------------------------------- hole -- +HOLECOLOR +1 2 r mul sub dup r D + +%---------------------------------------------------------------------- moon -- +MOONCOLOR +1 1 r D + +%---------------------------------------------------------------------- logo -- +LOGOCOLOR +LOGOFONT +F +LOGO stringwidth pop 2 div neg +-0.5 moveto +LOGO show + +%------------------------------------------------------------------------------ +% based on code from Blue Book Program 10, on pages 167--169 +% available at ftp://ftp.adobe.com/pub/adobe/displaypostscript/bluebook.shar + +% str ptsize centerangle radius outsidecircletext -- +/outsidecircletext { + circtextdict begin + /radius exch def + /centerangle exch def + /ptsize exch def + /str exch def + + gsave + str radius ptsize findhalfangle + centerangle + add rotate + str + { /charcode exch def + ( ) dup 0 charcode put outsideplacechar + } forall + + grestore + end +} def + +% string radius ptsize findhalfangle halfangle +/findhalfangle { + 4 div add + exch + stringwidth pop 2 div + exch + 2 mul 3.1415926535 mul div 360 mul +} def + +/circtextdict 16 dict def +circtextdict begin + + /outsideplacechar { + /char exch def + /halfangle char radius ptsize findhalfangle def + gsave + halfangle neg rotate + radius 0 translate + -90 rotate + char stringwidth pop 2 div neg 0 moveto + char show + grestore + halfangle 2 mul neg rotate + } def + +end + +%--------------------------------------------------------------------- label -- +LABELFONT +F + +/LABELSIZE LABELFONT exch pop def +/LABELRADIUS LABELSIZE 3 div 1 r add sub neg 1.02 mul def + + +/HALFANGLE + LABEL LABELRADIUS LABELSIZE findhalfangle + HALFDASHANGLE div ceiling HALFDASHANGLE mul +def + +/LABELANGLE + 60 LABELANGLE HALFANGLE sub + lt + { + HALFANGLE + HALFANGLE DASHANGLE div floor DASHANGLE mul + eq + {LABELANGLE DASHANGLE div ceiling DASHANGLE mul} + {LABELANGLE HALFDASHANGLE sub DASHANGLE div round DASHANGLE mul HALFDASHANGLE add} + ifelse + } + {HALFANGLE 60 add} + ifelse +def + +LABELCOLOR +LABEL +LABELSIZE +LABELANGLE +LABELRADIUS +outsidecircletext + +%--------------------------------------------------------------------- orbit -- +ORBITCOLOR +0.03 setlinewidth +[1 r add 3.1415926535 180 div HALFDASHANGLE mul mul] 0 setdash +newpath +0 0 +1 r add +3 copy +30 +LABELANGLE HALFANGLE add +arcn +stroke +60 +LABELANGLE HALFANGLE sub +2 copy +lt {arc stroke} {4 {pop} repeat} ifelse + +%------------------------------------------------------------------ copyright -- +/COPYRIGHT +(Graphic design by A. Nakonechnyj. Copyright (c) 1998, All rights reserved.) +def + +LABELCOLOR +LOGOFONT +32 div +F +2 sqrt 0.99 mul +dup +neg +moveto +COPYRIGHT +90 rotate +%show + +%---------------------------------------------------------------------- done -- +showpage + +%%Trailer +%%EOF diff --git a/lua-gd.spec b/lua-gd.spec new file mode 100644 index 000000000..dbd83ed14 --- /dev/null +++ b/lua-gd.spec @@ -0,0 +1,60 @@ +Name: lua-gd +Version: 2.0.33r3 +Release: 1 +Summary: gd bindings for the Lua programming language +Summary(pt_BR): Bindings da biblioteca gd para a linguagem Lua +Packager: Alexandre Erwin Ittner +License: MIT +Group: Libraries +Group(pt_BR): Bibliotecas +Source0: %{name}-%{version}.tar.gz +URL: http://lua-gd.luaforge.net/ +BuildRoot: %{_tmppath}/%{name}-%{version}-root +Requires: lua >= 5.1 +Requires: libgd2 >= 2.0.33 +BuildRequires: lua-devel +BuildRequires: libgd-devel >= 2.0.33 +Prefix: /usr +Provides: luagd + +%description +Lua-GD is a library that allows you to use the gd graphic library from +programs written in the Lua programming language. + + +%description -l pt_BR +Lua-GD ι uma biblioteca que permite usar a biblioteca grαfica gd em +programas escritos na linguagem Lua. + + +%prep +%setup -q + +%build +make + +%install +mkdir -p $RPM_BUILD_ROOT%{_libdir} +cp *.so $RPM_BUILD_ROOT%{_libdir} + +%clean +rm -rf %{buildroot} $RPM_BUILD_ROOT $RPM_BUILD_DIR/%{name}-%{version} + +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + +%files +%defattr(-,root,root) +%doc README COPYING doc/* demos +%{_libdir}/*.so* + +%changelog +* Sun Aug 28 2005 Alexandre Erwin Ittner +- First version of this package. +* Sun Apr 30 2006 Alexandre Erwin Ittner +- New version. License update. + + diff --git a/luagd-2.0.33r3-1.rockspec b/luagd-2.0.33r3-1.rockspec new file mode 100644 index 000000000..eeead4375 --- /dev/null +++ b/luagd-2.0.33r3-1.rockspec @@ -0,0 +1,43 @@ +package = "LuaGD" +version = "2.0.33r3-1" + +source = { + url = "https://github.com/ittner/lua-gd/archive/lua-gd-2.0.33r3.tar.gz", +} + +description = { + summary = "Lua binding to LibGD", + detailed = [[ +Lua-GD is a set of Lua bindings to the Thomas Boutell's gd library that +allows your code to quickly draw complete images with lines, polygons, arcs, +text, multiple colors, cut and paste from other images, flood fills, read in +or write out images in the PNG, JPEG or GIF format. It is not a kitchen-sink +graphics package, but it does include most frequently requested features, +including both truecolor and palette images, resampling (smooth resizing of +truecolor images) and so forth. It is particularly useful in Web applications. +]], + homepage = "http://ittner.github.io/lua-gd/", + license = "MIT/X11", + maintainer = "Alexandre Erwin Ittner" +} + +dependencies = { + "lua >= 5.1" +} + +external_dependencies = { + GD = { header = "gd.h" } +} + +build = { + type = "make", + platforms = { + unix = { + build_pass = true, + install_pass = false, + install = { lib = { "gd.so" } }, + copy_directories = { "doc", "demos" } + } + -- Some way to detect GD features on Windows? + } +} diff --git a/luagd.c b/luagd.c new file mode 100644 index 000000000..9730ff70a --- /dev/null +++ b/luagd.c @@ -0,0 +1,2361 @@ +/* + * luagd -- GD bindings for Lua. + * (c) 2004-13 Alexandre Erwin Ittner + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * If you use this package in a product, an acknowledgment in the product + * documentation would be greatly appreciated (but it is not required). + * + */ + +#include +#include +#include + +#include + +/* Standard gd fonts */ +#include +#include +#include +#include +#include + +#ifndef VERSION + #error "Trying to build without -DVERSION=xxx defined. Check the Makefile" +#endif +#define LIB_VERSION "lua-gd " VERSION +#define LIB_COPYRIGHT LIB_VERSION " (c) 2004-13 Alexandre Erwin Ittner" + +#define GD_IMAGE_PTR_TYPENAME "gdImagePtr_handle" + + +/* Compatibility between Lua 5.1 and Lua 5.2 */ +#if LUA_VERSION_NUM < 501 + #error "Unsuported Lua version. You must use Lua >= 5.1" +#endif + +#if LUA_VERSION_NUM < 502 + #define luaL_setfuncs(L, f, unused) { luaL_register(L, NULL, f); } + #define lua_rawlen lua_objlen +#endif + +#define boxptr(L, p) (*(void**)(lua_newuserdata(L, sizeof(void*))) = (p)) +#define unboxptr(L, i) (*(void**)(lua_touserdata(L, i))) + +/* Table assumed on top */ +#define tblseticons(L, c, v) \ + lua_pushliteral(L, c); \ + lua_pushnumber(L, v); \ + lua_settable(L, -3); + +/* Standard gd fonts */ +#define MY_GD_FONT_SMALL 0 +#define MY_GD_FONT_LARGE 1 +#define MY_GD_FONT_MEDIUM_BOLD 2 +#define MY_GD_FONT_GIANT 3 +#define MY_GD_FONT_TINY 4 + + +static int typerror(lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} + + +static gdImagePtr getImagePtr(lua_State *L, int i) { + if (luaL_checkudata(L, i, GD_IMAGE_PTR_TYPENAME) != NULL) { + gdImagePtr im = unboxptr(L, i); + if (im == NULL) + luaL_error(L, "attempt to use an invalid " GD_IMAGE_PTR_TYPENAME); + return im; + } + typerror(L, i, GD_IMAGE_PTR_TYPENAME); + return NULL; +} + + +static void pushImagePtr(lua_State *L, gdImagePtr im) { + boxptr(L, im); + luaL_getmetatable(L, GD_IMAGE_PTR_TYPENAME); + lua_setmetatable(L, -2); /* Done */ +} + + +static gdFontPtr getStdFont(lua_State *L, int i) { + int size; + + if (lua_isnumber(L, i) == 0) { + typerror(L, i, "Standard GD Font"); + return gdFontGetSmall(); + } + + size = luaL_checkinteger(L, i); + switch(size) { + case MY_GD_FONT_SMALL: + return gdFontGetSmall(); + + case MY_GD_FONT_LARGE: + return gdFontGetLarge(); + + case MY_GD_FONT_MEDIUM_BOLD: + return gdFontGetMediumBold(); + + case MY_GD_FONT_GIANT: + return gdFontGetGiant(); + + case MY_GD_FONT_TINY: + return gdFontGetTiny(); + + default: + typerror(L, i, "Standard GD Font"); + return gdFontGetSmall(); + } + + return gdFontGetSmall(); /* Not reached */ +} + + + + +/* + * Reads a Lua table and returns a pointer to a "gdFTStringExtra" struct. The + * table should have the following fields: + * + * { + * linespacing = 1.0, -- linespacing for '\n' + * charmap = gd.FTEX_Unicode, -- default charset + * hdpi = 96, -- horizontal resolution + * vdpi = 96, -- vertical resolution + * disable_kerning = true, -- disable kerning? + * xshow = true, -- return char positions? + * return_font_path_name = true, -- return font path names? + * fontconfig = true -- use fontconfig? + * } + * + */ +#ifdef GD_FREETYPE +static gdFTStringExtra *getFTStringExtraPtr(lua_State *L, int i) { + luaL_checktype(L, i, LUA_TTABLE); + + gdFTStringExtra *ex = (gdFTStringExtra*) malloc(sizeof(gdFTStringExtra)); + if (ex == NULL) + luaL_error(L, "Memory allocation failure"); + + ex->flags = 0; + + lua_pushstring(L, "linespacing"); + lua_gettable(L, i); + if (!lua_isnil(L, -1)) { + ex->flags |= gdFTEX_LINESPACE; + ex->linespacing = (double) lua_tonumber(L, -1); + } + lua_pop(L, 1); + + lua_pushstring(L, "charmap"); + lua_gettable(L, i); + if (!lua_isnil(L, -1)) { + ex->flags |= gdFTEX_CHARMAP;; + ex->charmap = (int) lua_tonumber(L, -1); + switch(ex->charmap) { + case gdFTEX_Unicode: + case gdFTEX_Shift_JIS: + case gdFTEX_Big5: + /* Future charsets here */ + break; + default: + free(ex); + luaL_error(L, "Invalid charset"); + } + } + lua_pop(L, 1); + + ex->hdpi = 96; + ex->vdpi = 96; + + lua_pushstring(L, "hdpi"); + lua_gettable(L, i); + if (!lua_isnil(L, -1)) { + ex->flags |= gdFTEX_RESOLUTION; + ex->hdpi = (double) lua_tonumber(L, -1); + } + lua_pop(L, 1); + + lua_pushstring(L, "vdpi"); + lua_gettable(L, i); + if (!lua_isnil(L, -1)) { + ex->flags |= gdFTEX_RESOLUTION; + ex->vdpi = (double) lua_tonumber(L, -1); + } + lua_pop(L, 1); + + lua_pushstring(L, "disable_kerning"); + lua_gettable(L, i); + if (lua_toboolean(L, -1)) + ex->flags |= gdFTEX_DISABLE_KERNING; + lua_pop(L, 1); + + lua_pushvalue(L, i); + lua_pushstring(L, "xshow"); + lua_gettable(L, i); + if (lua_toboolean(L, -1)) + ex->flags |= gdFTEX_XSHOW; + lua_pop(L, 1); + + lua_pushstring(L, "return_font_path_name"); + lua_gettable(L, i); + if (lua_toboolean(L, -1)) + ex->flags |= gdFTEX_RETURNFONTPATHNAME; + lua_pop(L, 1); + + lua_pushstring(L, "fontconfig"); + lua_gettable(L, i); + if (lua_toboolean(L, -1)) + ex->flags |= gdFTEX_FONTCONFIG; + lua_pop(L, 1); + + return ex; +} +#endif + + +/* gdImageCreate(int sx, int sy) */ +static int LgdImageCreate(lua_State *L) { + int sx, sy; + gdImagePtr im; + + sx = luaL_checkinteger(L, 1); + sy = luaL_checkinteger(L, 2); + im = gdImageCreate(sx, sy); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + +/* gdImageCreatePalette(int sx, int sy) */ +/* Useless? */ +static int LgdImageCreatePalette(lua_State *L) { + int sx, sy; + gdImagePtr im; + + sx = luaL_checkinteger(L, 1); + sy = luaL_checkinteger(L, 2); + im = gdImageCreatePalette(sx, sy); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + + + +/* gdImageCreateTrueColor(int sx, int sy) */ +static int LgdImageCreateTrueColor(lua_State *L) { + int sx, sy; + gdImagePtr im; + + sx = luaL_checkinteger(L, 1); + sy = luaL_checkinteger(L, 2); + im = gdImageCreateTrueColor(sx, sy); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + +/* gdImagePtr gdImageCreatePaletteFromTrueColor(gdImagePtr im, int ditherFlag, + int colorsWanted) */ +static int LgdImageCreatePaletteFromTrueColor(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int dither = lua_toboolean(L, 2); + int colors = luaL_checkinteger(L, 3); + gdImagePtr nim = gdImageCreatePaletteFromTrueColor(im, dither, colors); + + if (nim) + pushImagePtr(L, nim); + else + lua_pushnil(L); + return 1; +} + + +/* void gdImageTrueColorToPalette(gdImagePtr im, int ditherFlag, + int colorsWanted) */ +static int LgdImageTrueColorToPalette(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int dither = lua_toboolean(L, 2); + int colors = luaL_checkinteger(L, 3); + + gdImageTrueColorToPalette(im, dither, colors); + return 0; +} + + + +/* gdImageDestroy(gdImagePtr im) */ +static int LgdImageDestroy(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + if (im) + gdImageDestroy(im); + return 0; +} + +#ifdef GD_JPEG +/* gdImageCreateFromJpeg(FILE *in) */ +/* Changed to: gd.createFromJpeg(char *filename) */ +static int LgdImageCreateFromJpeg(lua_State *L) { + gdImagePtr im; + FILE *fp; + const char *fname = luaL_checkstring(L, 1); + + if (fname == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + if ((fp = fopen(fname, "rb")) == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromJpeg(fp); + fclose(fp); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + + +/* gdImageCreateFromJpegPtr(int size, void *data) */ +static int LgdImageCreateFromJpegPtr(lua_State *L) { + gdImagePtr im; + int size = lua_rawlen(L, 1); + void *str = (void*) luaL_checkstring(L, 1); + + if (str == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromJpegPtr(size, str); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} +#endif + + +#ifdef GD_GIF +/* gdImageCreateFromGif (FILE *in) */ +/* Changed to: gd.createFromGif (filename) */ +static int LgdImageCreateFromGif (lua_State *L) { + gdImagePtr im; + FILE *fp; + const char *fname = luaL_checkstring(L, 1); + + if (fname == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + if ((fp = fopen(fname, "rb")) == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromGif (fp); + fclose(fp); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + +/* gdImageCreateFromGifPtr(int size, void *data) */ +static int LgdImageCreateFromGifPtr(lua_State *L) { + gdImagePtr im; + int size = lua_rawlen(L, 1); + void *str = (void*) luaL_checkstring(L, 1); + + if (str == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromGifPtr(size, str); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} +#endif + + +#ifdef GD_PNG +/* gdImageCreateFromPng(FILE *in) */ +/* Changed to: gd.createFromPng(filename) */ +static int LgdImageCreateFromPng(lua_State *L) { + gdImagePtr im; + FILE *fp; + const char *fname = luaL_checkstring(L, 1); + + if (fname == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + if ((fp = fopen(fname, "rb")) == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromPng(fp); + fclose(fp); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + + +/* gdImageCreateFromPngPtr(int size, void *data) */ +static int LgdImageCreateFromPngPtr(lua_State *L) { + gdImagePtr im; + int size = lua_rawlen(L, 1); + void *str = (void*) luaL_checkstring(L, 1); + + if (str == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromPngPtr(size, str); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} +#endif + + +/* gdImageCreateFromGd(FILE *in) */ +/* Changed to: gd.createFromGd(filename) */ +static int LgdImageCreateFromGd(lua_State *L) { + gdImagePtr im; + FILE *fp; + const char *fname = luaL_checkstring(L, 1); + + if (fname == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + if ((fp = fopen(fname, "rb")) == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromGd(fp); + fclose(fp); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + + +/* gdImageCreateFromGdPtr(int size, void *data) */ +static int LgdImageCreateFromGdPtr(lua_State *L) { + gdImagePtr im; + int size = lua_rawlen(L, 1); + void *str = (void*) luaL_checkstring(L, 1); + + if (str == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromGdPtr(size, str); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + + +/* gdImageCreateFromGd2(FILE *in) */ +/* Changed to: gd.createFromGd2(filename) */ +static int LgdImageCreateFromGd2(lua_State *L) { + gdImagePtr im; + FILE *fp; + const char *fname = luaL_checkstring(L, 1); + + if (fname == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + if ((fp = fopen(fname, "rb")) == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromGd2(fp); + fclose(fp); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + + +/* gdImageCreateFromGd2Ptr(int size, void *data) */ +static int LgdImageCreateFromGd2Ptr(lua_State *L) { + gdImagePtr im; + int size = lua_rawlen(L, 1); + void *str = (void*) luaL_checkstring(L, 1); + + if (str == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromGd2Ptr(size, str); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + + +/* gdImageCreateFromGd2Part(FILE *in, int x, int y, int w, int h) */ +/* Changed to: gd.createFromGd2Part(filename, x, y, w, h)) */ +static int LgdImageCreateFromGd2Part(lua_State *L) { + gdImagePtr im; + FILE *fp; + const char *fname = luaL_checkstring(L, 1); + const int x = luaL_checkinteger(L, 2); + const int y = luaL_checkinteger(L, 3); + const int w = luaL_checkinteger(L, 4); + const int h = luaL_checkinteger(L, 5); + + if (fname == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + if ((fp = fopen(fname, "rb")) == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromGd2Part(fp, x, y, w, h); + fclose(fp); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + + +/* gdImageCreateFromGd2PartPtr(int size, void *data, + int srcX, int srcY, int w, int h) */ +static int LgdImageCreateFromGd2PartPtr(lua_State *L) { + gdImagePtr im; + int size = lua_rawlen(L, 1); + void *str = (void*) luaL_checkstring(L, 1); + const int x = luaL_checkinteger(L, 2); + const int y = luaL_checkinteger(L, 3); + const int w = luaL_checkinteger(L, 4); + const int h = luaL_checkinteger(L, 5); + + if (str == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromGd2PartPtr(size, str, x, y, w, h); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + + +#ifdef GD_XPM +/* gdImageCreateFromXbm(FILE *in) */ +/* Changed to: gd.createFromXbm(filename) */ +static int LgdImageCreateFromXbm(lua_State *L) { + gdImagePtr im; + FILE *fp; + const char *fname = luaL_checkstring(L, 1); + + if (fname == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + if ((fp = fopen(fname, "rb")) == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromXbm(fp); + fclose(fp); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} + + +/* gdImageCreateFromXpm(char *filename) */ +static int LgdImageCreateFromXpm(lua_State *L) { + gdImagePtr im; + char *fname = (char*) luaL_checkstring(L, 1); + + if (fname == NULL) { + lua_pushnil(L); + return 1; /* Error */ + } + im = gdImageCreateFromXpm(fname); + if (im != NULL) + pushImagePtr(L, im); + else + lua_pushnil(L); /* Error */ + return 1; +} +#endif + + + +#ifdef GD_JPEG +/* gdImageJpeg(gdImagePtr im, FILE *out, int quality) */ +/* Changed to: gd.jpeg(im, fname, quality) */ +static int LgdImageJpeg(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + const char *fname = luaL_checkstring(L, 2); + int quality = luaL_checkinteger(L, 3); + FILE *fp; + + if (fname == NULL) { + lua_pushboolean(L, 0); + return 1; + } + if ((fp = fopen(fname, "wb")) == NULL) { + lua_pushboolean(L, 0); + return 1; + } + gdImageJpeg(im, fp, quality); + fclose(fp); + lua_pushboolean(L, 1); + return 1; +} + + +/* void *gdImageJpegPtr(gdImagePtr im, int quality) */ +static int LgdImageJpegPtr(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int quality = luaL_checkinteger(L, 2); + char *str; + int size; + + str = gdImageJpegPtr(im, &size, quality); + if (str != NULL) { + lua_pushlstring(L, str, size); + gdFree(str); + } else { + lua_pushnil(L); /* Error */ + } + return 1; +} +#endif + +#ifdef GD_PNG +/* gdImagePng(gdImagePtr im, FILE *out) */ +/* Changed to: gd.png(im, fname) */ +static int LgdImagePng(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + const char *fname = luaL_checkstring(L, 2); + FILE *fp; + + if (fname == NULL) { + lua_pushboolean(L, 0); + return 1; + } + if ((fp = fopen(fname, "wb")) == NULL) { + lua_pushboolean(L, 0); + return 1; + } + gdImagePng(im, fp); + fclose(fp); + lua_pushboolean(L, 1); + return 1; +} + + +/* void *gdImagePngPtr(gdImagePtr im) */ +static int LgdImagePngPtr(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + char *str; + int size; + + str = gdImagePngPtr(im, &size); + if (str != NULL) { + lua_pushlstring(L, str, size); + gdFree(str); + } else { + lua_pushnil(L); /* Error */ + } + return 1; +} + + +/* gdImagePngEx(gdImagePtr im, FILE *out, int compression_level) */ +/* Changed to: gd.pngEx(im, fname, compression_level) */ +static int LgdImagePngEx(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + const char *fname = luaL_checkstring(L, 2); + int level = luaL_checkinteger(L, 3); + FILE *fp; + + if (fname == NULL) { + lua_pushboolean(L, 0); + return 1; + } + if ((fp = fopen(fname, "wb")) == NULL) { + lua_pushboolean(L, 0); + return 1; + } + gdImagePngEx(im, fp, level); + fclose(fp); + lua_pushboolean(L, 1); + return 1; +} + + +/* void *gdImagePngPtrEx(gdImagePtr im, int compression_level) */ +static int LgdImagePngPtrEx(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int level = luaL_checkinteger(L, 2); + char *str; + int size; + + str = gdImagePngPtrEx(im, &size, level); + if (str != NULL) { + lua_pushlstring(L, str, size); + gdFree(str); + } else { + lua_pushnil(L); /* Error */ + } + return 1; +} +#endif + + +#ifdef GD_GIF +/* gdImageGif (gdImagePtr im, FILE *out) */ +/* Changed to: gd.gif (im, fname) */ +static int LgdImageGif (lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + const char *fname = luaL_checkstring(L, 2); + FILE *fp; + + if (fname == NULL) { + lua_pushboolean(L, 0); + return 1; + } + if ((fp = fopen(fname, "wb")) == NULL) { + lua_pushboolean(L, 0); + return 1; + } + gdImageGif (im, fp); + fclose(fp); + lua_pushboolean(L, 1); + return 1; +} +#endif + +#ifdef GD_GIF +/* void *gdImageGifPtr(gdImagePtr im) */ +static int LgdImageGifPtr(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + char *str; + int size; + + str = gdImageGifPtr(im, &size); + if (str != NULL) { + lua_pushlstring(L, str, size); + gdFree(str); + } else { + lua_pushnil(L); /* Error */ + } + return 1; +} +#endif + + +/* gdImageGd(gdImagePtr im, FILE *out) */ +/* Changed to: gd.gd(im, fname) */ +static int LgdImageGd(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + const char *fname = luaL_checkstring(L, 2); + FILE *fp; + + if (fname == NULL) { + lua_pushboolean(L, 0); + return 1; + } + if ((fp = fopen(fname, "wb")) == NULL) { + lua_pushboolean(L, 0); + return 1; + } + gdImageGd(im, fp); + fclose(fp); + lua_pushboolean(L, 1); + return 1; +} + + +/* void *gdImageGdPtr(gdImagePtr im) */ +static int LgdImageGdPtr(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + char *str; + int size; + + str = gdImageGdPtr(im, &size); + if (str != NULL) { + lua_pushlstring(L, str, size); + gdFree(str); + } else { + lua_pushnil(L); /* Error */ + } + return 1; +} + + + +/* gdImageGd2(gdImagePtr im, FILE *out, int chunkSize, int fmt) */ +/* Changed to: gd.gd2(im, fname, chunkSize, fmt) */ +static int LgdImageGd2(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + const char *fname = luaL_checkstring(L, 2); + int cs = luaL_checkinteger(L, 3); + int fmt = luaL_checkinteger(L, 4); + FILE *fp; + + if (fname == NULL) { + lua_pushboolean(L, 0); + return 1; + } + if ((fp = fopen(fname, "wb")) == NULL) { + lua_pushboolean(L, 0); + return 1; + } + gdImageGd2(im, fp, cs, fmt); + fclose(fp); + lua_pushboolean(L, 1); + return 1; +} + + +/* void* gdImageGd2Ptr(gdImagePtr im, int chunkSize, int fmt, int *size) */ +static int LgdImageGd2Ptr(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int cs = luaL_checkinteger(L, 2); + int fmt = luaL_checkinteger(L, 3); + char *str; + int size; + + str = gdImageGd2Ptr(im, cs, fmt, &size); + if (str != NULL) { + lua_pushlstring(L, str, size); + gdFree(str); + } else + lua_pushnil(L); /* Error */ + return 1; +} + + +/* void gdImageWBMP(gdImagePtr im, int fg, FILE *out) */ +/* Changed to: gd.wbmp(im, int fg, filename) */ +static int LgdImageWBMP(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int fg = luaL_checkinteger(L, 2); + const char *fname = luaL_checkstring(L, 3); + FILE *fp; + + if (fname == NULL) { + lua_pushnil(L); + return 1; + } + if ((fp = fopen(fname, "wb")) == NULL) { + lua_pushnil(L); + return 1; + } + gdImageWBMP(im, fg, fp); + fclose(fp); + lua_pushboolean(L, 1); + return 1; +} + + +/* void* gdImageWBMPPtr(gdImagePtr im, int *size) */ +static int LgdImageWBMPPtr(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int fg = luaL_checkinteger(L, 2); + char *str; + int size; + + str = gdImageWBMPPtr(im, &size, fg); + if (str != NULL) { + lua_pushlstring(L, str, size); + gdFree(str); + } else { + lua_pushnil(L); /* Error */ + } + return 1; +} + + +/* int gdImageColorAllocate(gdImagePtr im, int r, int g, int b) */ +static int LgdImageColorAllocate(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int r = luaL_checkinteger(L, 2); + int g = luaL_checkinteger(L, 3); + int b = luaL_checkinteger(L, 4); + int c; + + c = gdImageColorAllocate(im, r, g, b); + if (c >= 0) + lua_pushnumber(L, c); /* ok */ + else + lua_pushnil(L); /* Can not allocate color */ + return 1; +} + + +/* int gdImageColorAllocateAlpha(gdImagePtr im, int r, int g, int b, int a) */ +static int LgdImageColorAllocateAlpha(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int r = luaL_checkinteger(L, 2); + int g = luaL_checkinteger(L, 3); + int b = luaL_checkinteger(L, 4); + int a = luaL_checkinteger(L, 5); + int c; + + c = gdImageColorAllocateAlpha(im, r, g, b, a); + if (c >= 0) + lua_pushnumber(L, c); /* ok */ + else + lua_pushnil(L); /* Can not allocate color */ + return 1; +} + + +/* int gdImageColorClosest(gdImagePtr im, int r, int g, int b) */ +static int LgdImageColorClosest(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int r = luaL_checkinteger(L, 2); + int g = luaL_checkinteger(L, 3); + int b = luaL_checkinteger(L, 4); + int c; + + c = gdImageColorClosest(im, r, g, b); + if (c >= 0) + lua_pushnumber(L, c); /* ok */ + else + lua_pushnil(L); /* Can not allocate color */ + return 1; +} + + +/* int gdImageColorClosestAlpha(gdImagePtr im, int r, int g, int b, int a) */ +static int LgdImageColorClosestAlpha(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int r = luaL_checkinteger(L, 2); + int g = luaL_checkinteger(L, 3); + int b = luaL_checkinteger(L, 4); + int a = luaL_checkinteger(L, 5); + int c; + + c = gdImageColorClosestAlpha(im, r, g, b, a); + if (c > 0) + lua_pushnumber(L, c); /* ok */ + else + lua_pushnil(L); /* Can not allocate color */ + return 1; +} + + +/* int gdImageColorClosestHWB(gdImagePtr im, int r, int g, int b) */ +static int LgdImageColorClosestHWB(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int r = luaL_checkinteger(L, 2); + int g = luaL_checkinteger(L, 3); + int b = luaL_checkinteger(L, 4); + int c; + + c = gdImageColorClosestHWB(im, r, g, b); + if (c >= 0) + lua_pushnumber(L, c); /* ok */ + else + lua_pushnil(L); /* Can not allocate color */ + return 1; +} + + +/* int gdImageColorExact(gdImagePtr im, int r, int g, int b) */ +static int LgdImageColorExact(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int r = luaL_checkinteger(L, 2); + int g = luaL_checkinteger(L, 3); + int b = luaL_checkinteger(L, 4); + int c; + + c = gdImageColorExact(im, r, g, b); + if (c >= 0) + lua_pushnumber(L, c); /* ok */ + else + lua_pushnil(L); /* Can not allocate color */ + return 1; +} + + +/* int gdImageColorExactAlpha(gdImagePtr im, int r, int g, int b, int a) */ +static int LgdImageColorExactAlpha(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int r = luaL_checkinteger(L, 2); + int g = luaL_checkinteger(L, 3); + int b = luaL_checkinteger(L, 4); + int a = luaL_checkinteger(L, 5); + int c; + + c = gdImageColorExactAlpha(im, r, g, b, a); + if (c >= 0) + lua_pushnumber(L, c); /* ok */ + else + lua_pushnil(L); /* Can not allocate color */ + return 1; +} + + +/* int gdImageColorResolve(gdImagePtr im, int r, int g, int b) */ +static int LgdImageColorResolve(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int r = luaL_checkinteger(L, 2); + int g = luaL_checkinteger(L, 3); + int b = luaL_checkinteger(L, 4); + int c; + + c = gdImageColorResolve(im, r, g, b); + if (c >= 0) + lua_pushnumber(L, c); /* ok */ + else + lua_pushnil(L); /* Can not allocate color */ + return 1; +} + + +/* int gdImageColorResolveAlpha(gdImagePtr im, int r, int g, int b, int a) */ +static int LgdImageColorResolveAlpha(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int r = luaL_checkinteger(L, 2); + int g = luaL_checkinteger(L, 3); + int b = luaL_checkinteger(L, 4); + int a = luaL_checkinteger(L, 5); + int c; + + c = gdImageColorResolveAlpha(im, r, g, b, a); + if (c >= 0) + lua_pushnumber(L, c); /* ok */ + else + lua_pushnil(L); /* Can not allocate color */ + return 1; +} + + +/* int gdImageColorsTotal(gdImagePtr im) */ +static int LgdImageColorsTotal(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + + lua_pushnumber(L, gdImageColorsTotal(im)); /* ok */ + return 1; +} + + +/* int gdImageRed(gdImagePtr im, int c) */ +static int LgdImageRed(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int c = luaL_checkinteger(L, 2); + + lua_pushnumber(L, gdImageRed(im, c)); /* ok */ + return 1; +} + +/* int gdImageBlue(gdImagePtr im, int c) */ +static int LgdImageBlue(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int c = luaL_checkinteger(L, 2); + + lua_pushnumber(L, gdImageBlue(im, c)); /* ok */ + return 1; +} + +/* int gdImageBlue(gdImagePtr im, int c) */ +static int LgdImageGreen(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int c = luaL_checkinteger(L, 2); + + lua_pushnumber(L, gdImageGreen(im, c)); /* ok */ + return 1; +} + +/* int gdImageAlpha(gdImagePtr im, int color) */ +static int LgdImageAlpha(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int c = luaL_checkinteger(L, 2); + lua_pushnumber(L, gdImageAlpha(im, c)); + return 1; +} + +/* int gdImageGetInterlaced(gdImagePtr im) */ +static int LgdImageGetInterlaced(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int ret = gdImageGetInterlaced(im); + + if (ret != 0) + lua_pushnumber(L, ret); + else + lua_pushnil(L); + return 1; +} + +/* int gdImageGetTransparent(gdImagePtr im) */ +static int LgdImageGetTransparent(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int ret = gdImageGetTransparent(im); + + if (ret != -1) + lua_pushnumber(L, ret); + else + lua_pushnil(L); + return 1; +} + + +/* void gdImageColorTransparent(gdImagePtr im, int c) */ +static int LgdImageColorTransparent(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int c = -1; + if (!lua_isnil(L, 2)) + c = luaL_checkinteger(L, 2); + gdImageColorTransparent(im, c); + return 0; +} + + +/* void gdImageColorDeallocate(gdImagePtr im, int c) */ +static int LgdImageColorDeallocate(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int c = luaL_checkinteger(L, 2); + gdImageColorDeallocate(im, c); + return 0; +} + + +/* int gdImageSX(gdImagePtr im) */ +static int LgdImageSX(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + lua_pushnumber(L, gdImageSX(im)); + return 1; +} + + +/* int gdImageSY(gdImagePtr im) */ +static int LgdImageSY(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + lua_pushnumber(L, gdImageSY(im)); + return 1; +} + + +/* Fear the power of the Moon!! --- x, y = im:sizeXY() */ +static int LgdImageSXY(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + lua_pushnumber(L, gdImageSX(im)); + lua_pushnumber(L, gdImageSY(im)); + return 2; +} + + +/* int gdImageBoundsSafe(gdImagePtr im, int x, int y) */ +static int LgdImageBoundsSafe(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int x = luaL_checkinteger(L, 2); + int y = luaL_checkinteger(L, 3); + + if (gdImageBoundsSafe(im, x, y) != 0) + lua_pushboolean(L, 1); + else + lua_pushboolean(L, 0); + return 1; +} + + +/* int gdImageGetPixel(gdImagePtr im, int x, int y) */ +static int LgdImageGetPixel(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int x = luaL_checkinteger(L, 2); + int y = luaL_checkinteger(L, 3); + + lua_pushnumber(L, gdImageGetPixel(im, x, y)); + return 1; +} + + +/* void gdImageSetPixel(gdImagePtr im, int x, int y, int color) */ +static int LgdImageSetPixel(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int x = luaL_checkinteger(L, 2); + int y = luaL_checkinteger(L, 3); + int c = luaL_checkinteger(L, 4); + + gdImageSetPixel(im, x, y, c); + return 0; +} + + +/* void gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int c) */ +static int LgdImageLine(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int x1 = luaL_checkinteger(L, 2); + int y1 = luaL_checkinteger(L, 3); + int x2 = luaL_checkinteger(L, 4); + int y2 = luaL_checkinteger(L, 5); + int c = luaL_checkinteger(L, 6); + + gdImageLine(im, x1, y1, x2, y2, c); + return 0; +} + +/* void gdImageRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, + int c) */ +static int LgdImageRectangle(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int x1 = luaL_checkinteger(L, 2); + int y1 = luaL_checkinteger(L, 3); + int x2 = luaL_checkinteger(L, 4); + int y2 = luaL_checkinteger(L, 5); + int c = luaL_checkinteger(L, 6); + + gdImageRectangle(im, x1, y1, x2, y2, c); + return 0; +} + + +/* void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, + int c) */ +static int LgdImageFilledRectangle(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int x1 = luaL_checkinteger(L, 2); + int y1 = luaL_checkinteger(L, 3); + int x2 = luaL_checkinteger(L, 4); + int y2 = luaL_checkinteger(L, 5); + int c = luaL_checkinteger(L, 6); + + gdImageFilledRectangle(im, x1, y1, x2, y2, c); + return 0; +} + + +/* Stack must have ONLY the table of points */ +static gdPoint *getPointList(lua_State *L, int *size) { + gdPoint *plist; + int i; + + luaL_checktype(L, -1, LUA_TTABLE); + *size = lua_rawlen(L, -1); + plist = (gdPoint*) malloc(*size * sizeof(gdPoint)); + + for (i = 0; i < *size; i++) { + /* Stack: T */ + lua_rawgeti(L, 1, i + 1); + + /* Stack: T, T' */ + if (lua_type(L, 2) != LUA_TTABLE) { + free(plist); + typerror(L, 2, "Point"); + } + + lua_rawgeti(L, 2, 1); + /* Stack: T, T', X */ + plist[i].x = luaL_checkinteger(L, -1); + lua_remove(L, -1); + + lua_rawgeti(L, 2, 2); + /* Stack: T, T', Y */ + plist[i].y = luaL_checkinteger(L, -1); + lua_remove(L, -1); + + /* Stack: T, T' */ + lua_remove(L, -1); + + /* Stack: T */ + } + + lua_remove(L, -1); + return plist; +} + + + +/* void gdImagePolygon(gdImagePtr im, gdPointPtr points, int pointsTotal, + int color) + Changed to: gd.polygon(im, { { x1, y1 }, { x2, y2 } ... }, color) */ +static int LgdImagePolygon(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + gdPoint *plist; + int size; + int c; + + c = luaL_checkinteger(L, 3); + lua_remove(L, 3); /* Get and drop color */ + lua_remove(L, 1); /* Drop image from the stack */ + plist = getPointList(L, &size); + gdImagePolygon(im, plist, size, c); + free(plist); + return 0; +} + + + +/* void gdImageFilledPolygon(gdImagePtr im, gdPointPtr points, + int pointsTotal, int color) + Changed to: gd.filledPolygon(im, { { x1, y1 }, { x2, y2 } ... }, color) */ +static int LgdImageFilledPolygon(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + gdPoint *plist; + int size; + int c; + + c = luaL_checkinteger(L, 3); + lua_remove(L, 3); /* Get and drop color */ + lua_remove(L, 1); /* Drop image from the stack */ + + plist = getPointList(L, &size); + gdImageFilledPolygon(im, plist, size, c); + free(plist); + return 0; +} + + +/* void gdImageOpenPolygon(gdImagePtr im, gdPointPtr points, + int pointsTotal, int color) + Changed to: gd.openPolygon(im, { { x1, y1 }, { x2, y2 } ... }, color) */ +static int LgdImageOpenPolygon(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + gdPoint *plist; + int size; + int c; + + c = luaL_checkinteger(L, 3); + lua_remove(L, 3); /* Get and drop color */ + lua_remove(L, 1); /* Drop image from the stack */ + + plist = getPointList(L, &size); + gdImageOpenPolygon(im, plist, size, c); + free(plist); + return 0; +} + + + +/* void gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, + int color) */ +static int LgdImageArc(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int cx = luaL_checkinteger(L, 2); + int cy = luaL_checkinteger(L, 3); + int w = luaL_checkinteger(L, 4); + int h = luaL_checkinteger(L, 5); + int s = luaL_checkinteger(L, 6); + int e = luaL_checkinteger(L, 7); + int c = luaL_checkinteger(L, 8); + + gdImageArc(im, cx, cy, w, h, s, e, c); + return 0; +} + + +/* void gdImageFilledArc(gdImagePtr im, int cx, int cy, int w, int h, + int s, int e, int color, int style) */ +static int LgdImageFilledArc(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int cx = luaL_checkinteger(L, 2); + int cy = luaL_checkinteger(L, 3); + int w = luaL_checkinteger(L, 4); + int h = luaL_checkinteger(L, 5); + int s = luaL_checkinteger(L, 6); + int e = luaL_checkinteger(L, 7); + int c = luaL_checkinteger(L, 8); + int sty = luaL_checkinteger(L, 9); + + gdImageFilledArc(im, cx, cy, w, h, s, e, c, sty); + return 0; +} + + +/* void gdImageFilledEllipse(gdImagePtr im, int cx, int cy, int w, int h, + int color) */ +static int LgdImageFilledEllipse(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int cx = luaL_checkinteger(L, 2); + int cy = luaL_checkinteger(L, 3); + int w = luaL_checkinteger(L, 4); + int h = luaL_checkinteger(L, 5); + int c = luaL_checkinteger(L, 6); + + gdImageFilledEllipse(im, cx, cy, w, h, c); + return 0; +} + + +/* void gdImageFill(gdImagePtr im, int x, int y, int color) */ +static int LgdImageFill(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int x = luaL_checkinteger(L, 2); + int y = luaL_checkinteger(L, 3); + int c = luaL_checkinteger(L, 4); + + gdImageFill(im, x, y, c); + return 0; +} + + +/* void gdImageFillToBorder(gdImagePtr im, int x, int y, int border, + int color) */ +static int LgdImageFillToBorder(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int x = luaL_checkinteger(L, 2); + int y = luaL_checkinteger(L, 3); + int b = luaL_checkinteger(L, 4); + int c = luaL_checkinteger(L, 5); + + gdImageFillToBorder(im, x, y, b, c); + return 0; +} + + +/* void gdImageSetAntiAliased(gdImagePtr im, int c) */ +static int LgdImageSetAntiAliased(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int c = luaL_checkinteger(L, 2); + + gdImageSetAntiAliased(im, c); + return 0; +} + + +/* void gdImageSetAntiAliasedDontBlend(gdImagePtr im, int c) */ +static int LgdImageSetAntiAliasedDontBlend(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int c = luaL_checkinteger(L, 2); + + gdImageSetAntiAliasedDontBlend(im, c, 1); + return 0; +} + + +/* void gdImageSetBrush(gdImagePtr im, gdImagePtr brush) */ +static int LgdImageSetBrush(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + gdImagePtr b = getImagePtr(L, 2); + + gdImageSetBrush(im, b); + return 0; +} + + +/* void gdImageSetTile(gdImagePtr im, gdImagePtr tile) */ +static int LgdImageSetTile(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + gdImagePtr t = getImagePtr(L, 1); + + gdImageSetTile(im, t); + return 0; +} + + +/* void gdImageSetStyle(gdImagePtr im, int *style, int styleLength) */ +/* Changed To: gd.setStyle(im, { c1, c2, c3, ... } ) */ +static int LgdImageSetStyle(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int *slist; + int size; + int i; + + /* Stack: Im, T */ + luaL_checktype(L, -1, LUA_TTABLE); + size = lua_rawlen(L, -1); + slist = (int*) malloc(size * sizeof(int)); + + for (i = 0; i < size; i++) { + /* Stack: Im, T */ + lua_rawgeti(L, 2, i + 1); + + /* Stack: Im, T, num */ + if (lua_type(L, -1) != LUA_TNUMBER) { + free(slist); + typerror(L, -1, "Number"); + } + + slist[i] = luaL_checkinteger(L, -1); + lua_remove(L, -1); + + /* Stack: Im, T */ + } + + gdImageSetStyle(im, slist, size); + free(slist); + return 0; +} + + +/* void gdImageSetThickness(gdImagePtr im, int thickness) */ +static int LgdImageSetThickness(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int t = luaL_checkinteger(L, 2); + + gdImageSetThickness(im, t); + return 0; +} + + +/* void gdImageAlphaBlending(gdImagePtr im, int blending) */ +/* Changed to: im:alphaBlending(true_or_false) */ +static int LgdImageAlphaBlending(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int b = lua_toboolean(L, 2); + + gdImageAlphaBlending(im, b); + return 0; +} + + +/* void gdImageSaveAlpha(gdImagePtr im, int saveFlag) */ +/* Changed to: im:saveAlpha(true_or_false) */ +static int LgdImageSaveAlpha(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int b = lua_toboolean(L, 2); + + gdImageSaveAlpha(im, b); + return 0; +} + + +/* gdImageInterlace(gdImagePtr im, int interlace) */ +/* Changed to: im:interlace(true_or_false) */ +static int LgdImageInterlace(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int i = lua_toboolean(L, 2); + + gdImageInterlace(im, i); + return 0; +} + + +/* void gdImageString(gdImagePtr im, gdFontPtr font, int x, int y, + unsigned char *s, int color) */ +static int LgdImageString(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + gdFontPtr fnt = getStdFont(L, 2); + int x = luaL_checkinteger(L, 3); + int y = luaL_checkinteger(L, 4); + unsigned char *str = (unsigned char*) luaL_checkstring(L, 5); + int c = luaL_checkinteger(L, 6); + + gdImageString(im, fnt, x, y, str, c); + return 0; +} + + +/* void gdImageStringUp(gdImagePtr im, gdFontPtr font, int x, int y, + unsigned char *s, int color) */ +static int LgdImageStringUp(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + gdFontPtr fnt = getStdFont(L, 2); + int x = luaL_checkinteger(L, 3); + int y = luaL_checkinteger(L, 4); + unsigned char *str = (unsigned char*) luaL_checkstring(L, 5); + int c = luaL_checkinteger(L, 6); + + gdImageStringUp(im, fnt, x, y, str, c); + return 0; +} + + +/* void gdImageChar(gdImagePtr im, gdFontPtr font, int x, int y, + int c, int color) */ +/* Useless? */ +static int LgdImageChar(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + gdFontPtr fnt = getStdFont(L, 2); + int x = luaL_checkinteger(L, 3); + int y = luaL_checkinteger(L, 4); + char *str = (char*) luaL_checkstring(L, 5); + int c = luaL_checkinteger(L, 6); + int chr; + + if (str) { + chr = (int) str[0]; + } else { + typerror(L, 5, "string"); + return 0; + } + + gdImageChar(im, fnt, x, y, chr, c); + return 0; +} + + +/* void gdImageCharUp(gdImagePtr im, gdFontPtr font, int x, int y, + int c, int color) */ +static int LgdImageCharUp(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + gdFontPtr fnt = getStdFont(L, 2); + int x = luaL_checkinteger(L, 3); + int y = luaL_checkinteger(L, 4); + char *str = (char*) luaL_checkstring(L, 5); + int c = luaL_checkinteger(L, 6); + int chr; + + if (str) + chr = (int) str[0]; + else { + typerror(L, 5, "string"); + return 0; + } + + gdImageCharUp(im, fnt, x, y, chr, c); + return 0; +} + + +/* void gdImageCopy(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, + int srcX, int srcY, int w, int h) */ +static int LgdImageCopy(lua_State *L) { + gdImagePtr dst = getImagePtr(L, 1); + gdImagePtr src = getImagePtr(L, 2); + int dstX = luaL_checkinteger(L, 3); + int dstY = luaL_checkinteger(L, 4); + int srcX = luaL_checkinteger(L, 5); + int srcY = luaL_checkinteger(L, 6); + int w = luaL_checkinteger(L, 7); + int h = luaL_checkinteger(L, 8); + + gdImageCopy(dst, src, dstX, dstY, srcX, srcY, w, h); + return 0; +} + + + +/* void gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, + int dstY, int srcX, int srcY, int destW, int destH, + int srcW, int srcH) */ +static int LgdImageCopyResized(lua_State *L) { + gdImagePtr dst = getImagePtr(L, 1); + gdImagePtr src = getImagePtr(L, 2); + int dstX = luaL_checkinteger(L, 3); + int dstY = luaL_checkinteger(L, 4); + int srcX = luaL_checkinteger(L, 5); + int srcY = luaL_checkinteger(L, 6); + int dstW = luaL_checkinteger(L, 7); + int dstH = luaL_checkinteger(L, 8); + int srcW = luaL_checkinteger(L, 9); + int srcH = luaL_checkinteger(L, 10); + + gdImageCopyResized(dst, src, dstX, dstY, srcX, srcY, dstW, dstH, + srcW, srcH); + return 0; +} + + +/* void gdImageCopyResampled(gdImagePtr dst, gdImagePtr src, int dstX, + int dstY, int srcX, int srcY, int destW, int destH, int srcW, + int srcH) */ +static int LgdImageCopyResampled(lua_State *L) { + gdImagePtr dst = getImagePtr(L, 1); + gdImagePtr src = getImagePtr(L, 2); + int dstX = luaL_checkinteger(L, 3); + int dstY = luaL_checkinteger(L, 4); + int srcX = luaL_checkinteger(L, 5); + int srcY = luaL_checkinteger(L, 6); + int dstW = luaL_checkinteger(L, 7); + int dstH = luaL_checkinteger(L, 8); + int srcW = luaL_checkinteger(L, 9); + int srcH = luaL_checkinteger(L, 10); + + gdImageCopyResampled(dst, src, dstX, dstY, srcX, srcY, dstW, dstH, + srcW, srcH); + return 0; +} + + +/* void gdImageCopyRotated(gdImagePtr dst, gdImagePtr src, double dstX, + double dstY, int srcX, int srcY, int srcW, int srcH, int angle) */ +static int LgdImageCopyRotated(lua_State *L) { + gdImagePtr dst = getImagePtr(L, 1); + gdImagePtr src = getImagePtr(L, 2); + double dstX = (double) lua_tonumber(L, 3); + double dstY = (double) lua_tonumber(L, 4); + int srcX = luaL_checkinteger(L, 5); + int srcY = luaL_checkinteger(L, 6); + int srcW = luaL_checkinteger(L, 7); + int srcH = luaL_checkinteger(L, 8); + int ang = luaL_checkinteger(L, 9); + + gdImageCopyRotated(dst, src, dstX, dstY, srcX, srcY, srcW, srcH, ang); + return 0; +} + + +/* void gdImageCopyMerge(gdImagePtr dst, gdImagePtr src, int dstX, + int dstY, int srcX, int srcY, int w, int h, int pct) */ +static int LgdImageCopyMerge(lua_State *L) { + gdImagePtr dst = getImagePtr(L, 1); + gdImagePtr src = getImagePtr(L, 2); + int dstX = luaL_checkinteger(L, 3); + int dstY = luaL_checkinteger(L, 4); + int srcX = luaL_checkinteger(L, 5); + int srcY = luaL_checkinteger(L, 6); + int w = luaL_checkinteger(L, 7); + int h = luaL_checkinteger(L, 8); + int pct = luaL_checkinteger(L, 9); + + gdImageCopyMerge(dst, src, dstX, dstY, srcX, srcY, w, h, pct); + return 0; +} + + +/* void gdImageCopyMergeGray(gdImagePtr dst, gdImagePtr src, int dstX, + int dstY, int srcX, int srcY, int w, int h, int pct) */ +static int LgdImageCopyMergeGray(lua_State *L) { + gdImagePtr dst = getImagePtr(L, 1); + gdImagePtr src = getImagePtr(L, 2); + int dstX = luaL_checkinteger(L, 3); + int dstY = luaL_checkinteger(L, 4); + int srcX = luaL_checkinteger(L, 5); + int srcY = luaL_checkinteger(L, 6); + int w = luaL_checkinteger(L, 7); + int h = luaL_checkinteger(L, 8); + int pct = luaL_checkinteger(L, 9); + + gdImageCopyMergeGray(dst, src, dstX, dstY, srcX, srcY, w, h, pct); + return 0; +} + + +/* void gdImagePaletteCopy(gdImagePtr dst, gdImagePtr src) */ +static int LgdImagePaletteCopy(lua_State *L) { + gdImagePtr dst = getImagePtr(L, 1); + gdImagePtr src = getImagePtr(L, 2); + + gdImagePaletteCopy(dst, src); + return 0; +} + + +/* void gdImageSquareToCircle(gdImagePtr im, int radius) */ +static int LgdImageSquareToCircle(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int r = luaL_checkinteger(L, 2); + + gdImageSquareToCircle(im, r); + return 0; +} + + +/* void gdImageSharpen(gdImagePtr im, int pct) */ +static int LgdImageSharpen(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int pct = luaL_checkinteger(L, 2); + + gdImageSharpen(im, pct); + return 0; +} + + +/* void gdImageSetClip(gdImagePtr im, int x1, int y1, int x2, int y2) */ +static int LgdImageSetClip(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int x1 = luaL_checkinteger(L, 2); + int y1 = luaL_checkinteger(L, 3); + int x2 = luaL_checkinteger(L, 4); + int y2 = luaL_checkinteger(L, 5); + + gdImageSetClip(im, x1, y1, x2, y2); + return 0; +} + + +/* void gdImageGetClip(gdImagePtr im, int *x1, int *y1, int *x2, int *y2) */ +/* Changed to: x1p, y1p, x2p, y2p = im:getClip() */ +static int LgdImageGetClip(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int x1 = 0, y1 = 0, x2 = 0, y2 = 0; + + gdImageGetClip(im, &x1, &y1, &x2, &y2); + lua_pushnumber(L, x1); + lua_pushnumber(L, y1); + lua_pushnumber(L, x2); + lua_pushnumber(L, y2); + return 4; +} + + +#ifdef GD_FONTCONFIG +/* int gdFTUseFontConfig(int flag) */ +/* Changed to: gd.useFontConfig(true_or_false) */ +static int LgdFTUseFontConfig(lua_State *L) { + int b = lua_toboolean(L, 1); + lua_pushboolean(L, gdFTUseFontConfig(b)); + return 1; +} +#endif + + +#ifdef GD_FREETYPE +/* int gdFontCacheSetup(void) */ +static int LgdFontCacheSetup(lua_State *L) { + lua_pushboolean(L, !gdFontCacheSetup()); + return 1; +} + + +/* void gdFontCacheShutdown(void) */ +static int LgdFontCacheShutdown(lua_State *L) { + gdFontCacheShutdown(); + return 0; +} +#endif + + +/* char *gdImageStringFT(gdImagePtr im, int *brect, int fg, char *fontname, + double ptsize, double angle, int x, int y, char *string) + + Changed To: + llX, llY, lrX, lrY, urX, urY, ulX, ulY = im:stringFT(fg, fontname, + ptsize, angle, x, y, string) + + Or (to get the points only): + llX, llY, lrX, lrY, urX, urY, ulX, ulY = gd.stringFT(nil, fg, + fontname, ptsize, angle, x, y, string) +*/ + +#ifdef GD_FREETYPE +static int LgdImageStringFT(lua_State *L) { + gdImagePtr im; + int fg = luaL_checkinteger(L, 2); + char *font = (char*) luaL_checkstring(L, 3); + double size = (double) lua_tonumber(L, 4); + double ang = (double) lua_tonumber(L, 5); + int x = luaL_checkinteger(L, 6); + int y = luaL_checkinteger(L, 7); + char *str = (char*) luaL_checkstring(L, 8); + int brect[8]; + + if (lua_isnil(L, 1)) + im = NULL; + else + im = getImagePtr(L, 1); + + if (gdImageStringFT(im, brect, fg, font, size, ang, x, y, str) == NULL) { + lua_pushnumber(L, brect[0]); + lua_pushnumber(L, brect[1]); + lua_pushnumber(L, brect[2]); + lua_pushnumber(L, brect[3]); + lua_pushnumber(L, brect[4]); + lua_pushnumber(L, brect[5]); + lua_pushnumber(L, brect[6]); + lua_pushnumber(L, brect[7]); + return 8; + } + + lua_pushnil(L); + return 1; +} + + +/* char *gdImageStringFT(gdImagePtr im, int *brect, int fg, char *fontname, + double ptsize, double angle, int x, int y, char *string) + + Changed To: + llX, llY, lrX, lrY, urX, urY, ulX, ulY [, xshow] [, fontpath] = + im:stringFTEx(fg, fontname, ptsize, angle, x, y, string, { ... }) + + Or: + llX, llY, lrX, lrY, urX, urY, ulX, ulY [, xshow] [, fontpath] = + gd.stringFTEx(nil, fg, fontname, ptsize, angle, x, y, string, { ... }) + +*/ + +static int LgdImageStringFTEx(lua_State *L) { + gdImagePtr im; + int fg = luaL_checkinteger(L, 2); + char *font = (char*) luaL_checkstring(L, 3); + double size = (double) lua_tonumber(L, 4); + double ang = (double) lua_tonumber(L, 5); + int x = luaL_checkinteger(L, 6); + int y = luaL_checkinteger(L, 7); + char *str = (char*) luaL_checkstring(L, 8); + gdFTStringExtra *ex = getFTStringExtraPtr(L, 9); + int brect[8]; + int ret = 8; + + if (lua_isnil(L, 1)) + im = NULL; + else + im = getImagePtr(L, 1); + + if (gdImageStringFTEx(im, brect, fg, font, size, ang, x, y, str, ex) == NULL) { + lua_pushnumber(L, brect[0]); + lua_pushnumber(L, brect[1]); + lua_pushnumber(L, brect[2]); + lua_pushnumber(L, brect[3]); + lua_pushnumber(L, brect[4]); + lua_pushnumber(L, brect[5]); + lua_pushnumber(L, brect[6]); + lua_pushnumber(L, brect[7]); + ret = 8; + if (ex->flags & gdFTEX_XSHOW) { + lua_pushstring(L, ex->xshow); + gdFree(ex->xshow); + ret++; + } + if (ex->flags & gdFTEX_RETURNFONTPATHNAME) { + lua_pushstring(L, ex->fontpath); + gdFree(ex->fontpath); + ret++; + } + + free(ex); + return ret; + } + + lua_pushnil(L); + return 1; +} + + + +/* char *gdImageStringFTCircle(gdImagePtr im, int cx, int cy, double radius, + double textRadius, double fillPortion, char *font, + double points, char *top, char *bottom, int fgcolor) + + Changed to: im:stringFTCircle(cx, cy, radius, textRadius, + fillPortion, fontname, points, top, bottom, color) +*/ + +static int LgdImageStringFTCircle(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int cx = luaL_checkinteger(L, 2); + int cy = luaL_checkinteger(L, 3); + double radius = (double) lua_tonumber(L, 4); + double textRadius = (double) lua_tonumber(L, 5); + double fillPortion = (double) lua_tonumber(L, 6); + char *font = (char*) luaL_checkstring(L, 7); + double points = (double) lua_tonumber(L, 8); + char *top = (char*) luaL_checkstring(L, 9); + char *bottom = (char*) luaL_checkstring(L, 10); + int color = luaL_checkinteger(L, 11); + + if (gdImageStringFTCircle(im, cx, cy, radius, textRadius, fillPortion, + font, points, top, bottom, color)) + lua_pushboolean(L, 0); /* Error */ + else + lua_pushboolean(L, 1); + return 1; +} +#endif + + +#ifdef GD_GIF +/* void gdImageGifAnimBegin(gdImagePtr im, FILE *out, int GlobalCM, int Loops) + Changed to: im:gifAnimBegin(filename, globalCM, loops) +*/ + +static int LgdImageGifAnimBegin(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + const char *fname = luaL_checkstring(L, 2); + int globalCM = lua_toboolean(L, 3); + int loops = luaL_checkinteger(L, 4); + FILE *fp; + + if ((fp = fopen(fname, "wb")) == NULL) { + lua_pushboolean(L, 0); /* Error */ + return 1; + } + + gdImageGifAnimBegin(im, fp, globalCM, loops); + fclose(fp); + lua_pushboolean(L, 1); /* ok */ + return 1; +} + + +/* void gdImageGifAnimAdd(gdImagePtr im, FILE *out, int LocalCM, int LeftOfs, + int TopOfs, int Delay, int Disposal, gdImagePtr previm) + Changed to: im:gifAnimAdd(filename, localCM, leftOfs, topOfs, delay, + disposal [, previm]) +*/ + +static int LgdImageGifAnimAdd(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + const char *fname = luaL_checkstring(L, 2); + int localCM = lua_toboolean(L, 3); + int leftOfs = luaL_checkinteger(L, 4); + int topOfs = luaL_checkinteger(L, 5); + int delay = luaL_checkinteger(L, 6); + int disp = luaL_checkinteger(L, 7); + gdImagePtr previm = NULL; + FILE *fp; + + if (lua_gettop(L) >= 8) + previm = getImagePtr(L, 8); + + if ((fp = fopen(fname, "ab")) == NULL) { + lua_pushboolean(L, 0); /* Error */ + return 1; + } + + gdImageGifAnimAdd(im, fp, localCM, leftOfs, topOfs, delay, disp, previm); + fclose(fp); + lua_pushboolean(L, 1); /* ok */ + return 1; +} + + +/* void gdImageGifAnimEnd(FILE *out) + Changed to: gd.gifAnimEnd(filename) +*/ + +static int LgdImageGifAnimEnd(lua_State *L) { + const char *fname = luaL_checkstring(L, 1); + FILE *fp; + + if ((fp = fopen(fname, "ab")) == NULL) { + lua_pushboolean(L, 0); /* Error */ + return 1; + } + + gdImageGifAnimEnd(fp); + fclose(fp); + lua_pushboolean(L, 1); /* ok */ + return 1; +} + + +/* void* gdImageGifAnimBeginPtr(gdImagePtr im, int *size, int GlobalCM, + int Loops) + Changed to: im:gifAnimBeginStr(globalCM, loops) +*/ + +static int LgdImageGifAnimBeginPtr(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int globalCM = lua_toboolean(L, 2); + int loops = luaL_checkinteger(L, 3); + char *str; + int size; + + str = gdImageGifAnimBeginPtr(im, &size, globalCM, loops); + if (str != NULL) { + lua_pushlstring(L, str, size); + gdFree(str); + } else + lua_pushnil(L); + return 1; +} + + +/* void* gdImageGifAnimAddPtr(gdImagePtr im, int *size, int LocalCM, + int LeftOfs, int TopOfs, int Delay, int Disposal, gdImagePtr previm) + + Changed to: im:gifAnimAddStr(localCM, leftOfs, topOfs, delay, + disposal [, previm]) +*/ + +static int LgdImageGifAnimAddPtr(lua_State *L) { + gdImagePtr im = getImagePtr(L, 1); + int localCM = lua_toboolean(L, 2); + int leftOfs = luaL_checkinteger(L, 3); + int topOfs = luaL_checkinteger(L, 4); + int delay = luaL_checkinteger(L, 5); + int disp = luaL_checkinteger(L, 6); + gdImagePtr previm = NULL; + int size; + char *str; + + if (lua_gettop(L) >= 7) + previm = getImagePtr(L, 7); + + str = gdImageGifAnimAddPtr(im, &size, localCM, leftOfs, topOfs, delay, + disp, previm); + if (str != NULL) { + lua_pushlstring(L, str, size); + gdFree(str); + } else + lua_pushnil(L); + return 1; +} + + +/* void* gdImageGifAnimEndPtr(int *size) + Changed to: gd.gifAnimEndStr() +*/ + +static int LgdImageGifAnimEndPtr(lua_State *L) { + int size; + char *str; + + str = gdImageGifAnimEndPtr(&size); + if (str != NULL) { + lua_pushlstring(L, str, size); + gdFree(str); + } else + lua_pushnil(L); + return 1; +} + +#endif + + + +static const luaL_Reg LgdFunctions[] = +{ +/* Leave Lua do it! + { "destroy", LgdImageDestroy }, */ + + { "create", LgdImageCreate }, + { "createPalette", LgdImageCreatePalette }, + { "createTrueColor", LgdImageCreateTrueColor }, + { "createPaletteFromTrueColor", LgdImageCreatePaletteFromTrueColor }, + { "trueColorToPalette", LgdImageTrueColorToPalette }, + +#ifdef GD_JPEG + { "createFromJpeg", LgdImageCreateFromJpeg }, + { "createFromJpegStr", LgdImageCreateFromJpegPtr }, +#endif +#ifdef GD_GIF + { "createFromGif", LgdImageCreateFromGif }, + { "createFromGifStr", LgdImageCreateFromGifPtr }, +#endif + { "createFromPng", LgdImageCreateFromPng }, + { "createFromPngStr", LgdImageCreateFromPngPtr }, + { "createFromGd", LgdImageCreateFromGd }, + { "createFromGdStr", LgdImageCreateFromGdPtr }, + { "createFromGd2", LgdImageCreateFromGd2 }, + { "createFromGd2Str", LgdImageCreateFromGd2Ptr }, + { "createFromGd2Part", LgdImageCreateFromGd2Part }, + { "createFromGd2PartStr", LgdImageCreateFromGd2PartPtr }, + +#ifdef GD_XPM + { "createFromXbm", LgdImageCreateFromXbm }, + { "createFromXpm", LgdImageCreateFromXpm }, +#endif + +#ifdef GD_JPEG + { "jpeg", LgdImageJpeg }, + { "jpegStr", LgdImageJpegPtr }, +#endif +#ifdef GD_PNG + { "png", LgdImagePng }, + { "pngStr", LgdImagePngPtr }, + { "pngEx", LgdImagePngEx }, + { "pngStrEx", LgdImagePngPtrEx }, +#endif +#ifdef GD_GIF + { "gif", LgdImageGif }, + { "gifStr", LgdImageGifPtr }, +#endif + { "gd", LgdImageGd }, + { "gdStr", LgdImageGdPtr }, + { "gd2", LgdImageGd2 }, + { "gd2Str", LgdImageGd2Ptr }, + { "wbmp", LgdImageWBMP }, + { "wbmpStr", LgdImageWBMPPtr }, + + { "colorAllocate", LgdImageColorAllocate }, + { "colorAllocateAlpha", LgdImageColorAllocateAlpha }, + { "colorClosest", LgdImageColorClosest }, + { "colorClosestAlpha", LgdImageColorClosestAlpha }, + { "colorClosestHWB", LgdImageColorClosestHWB }, + { "colorExact", LgdImageColorExact }, + { "colorExactAlpha", LgdImageColorExactAlpha }, + { "colorResolve", LgdImageColorResolve }, + { "colorResolveAlpha", LgdImageColorResolveAlpha }, + { "colorsTotal", LgdImageColorsTotal }, + { "red", LgdImageRed }, + { "blue", LgdImageBlue }, + { "green", LgdImageGreen }, + { "alpha", LgdImageAlpha }, + { "getTransparent", LgdImageGetTransparent }, + { "colorTransparent", LgdImageColorTransparent }, + { "colorDeallocate", LgdImageColorDeallocate }, + + { "boundsSafe", LgdImageBoundsSafe }, + { "getPixel", LgdImageGetPixel }, + { "sizeX", LgdImageSX }, + { "sizeY", LgdImageSY }, + { "sizeXY", LgdImageSXY }, + { "getClip", LgdImageGetClip }, + { "setClip", LgdImageSetClip }, + + { "setPixel", LgdImageSetPixel }, + { "line", LgdImageLine }, + { "rectangle", LgdImageRectangle }, + { "filledRectangle", LgdImageFilledRectangle }, + { "polygon", LgdImagePolygon }, + { "filledPolygon", LgdImageFilledPolygon }, + { "openPolygon", LgdImageOpenPolygon }, + { "arc", LgdImageArc }, + { "filledArc", LgdImageFilledArc }, + { "filledEllipse", LgdImageFilledEllipse }, + { "fill", LgdImageFill }, + { "fillToBorder", LgdImageFillToBorder }, + + { "setAntiAliased", LgdImageSetAntiAliased }, + { "setAntiAliasedDontBlend", LgdImageSetAntiAliasedDontBlend }, + { "setBrush", LgdImageSetBrush }, + { "setTile", LgdImageSetTile }, + { "setStyle", LgdImageSetStyle }, + { "setThickness", LgdImageSetThickness }, + { "alphaBlending", LgdImageAlphaBlending }, + { "saveAlpha", LgdImageSaveAlpha }, + { "getInterlaced", LgdImageGetInterlaced }, + { "interlace", LgdImageInterlace }, + + { "string", LgdImageString }, + { "stringUp", LgdImageStringUp }, + { "char", LgdImageChar }, + { "charUp", LgdImageCharUp }, + + { "copy", LgdImageCopy }, + { "copyResized", LgdImageCopyResized }, + { "copyResampled", LgdImageCopyResampled }, + { "copyRotated", LgdImageCopyRotated }, + { "copyMerge", LgdImageCopyMerge }, + { "copyMergeGray", LgdImageCopyMergeGray }, + { "paletteCopy", LgdImagePaletteCopy }, + { "squareToCircle", LgdImageSquareToCircle }, + { "sharpen", LgdImageSharpen }, + +#ifdef GD_FREETYPE + { "stringFT", LgdImageStringFT }, + { "stringFTEx", LgdImageStringFTEx }, + { "stringFTCircle", LgdImageStringFTCircle }, + { "fontCacheSetup", LgdFontCacheSetup }, + { "fontCacheShutdown", LgdFontCacheShutdown }, +#endif + +#ifdef GD_FONTCONFIG + { "useFontConfig", LgdFTUseFontConfig }, +#endif + +#ifdef GD_GIF /* Gif animation */ + { "gifAnimBegin", LgdImageGifAnimBegin }, + { "gifAnimAdd", LgdImageGifAnimAdd }, + { "gifAnimEnd", LgdImageGifAnimEnd }, + { "gifAnimBeginStr", LgdImageGifAnimBeginPtr }, + { "gifAnimAddStr", LgdImageGifAnimAddPtr }, + { "gifAnimEndStr", LgdImageGifAnimEndPtr }, +#endif + + { NULL, NULL } +}; + + +static const luaL_Reg LgdMetatable[] = +{ + { "__gc", LgdImageDestroy }, + { NULL, NULL } +}; + + +int luaopen_gd(lua_State *L) { + lua_newtable(L); + luaL_setfuncs(L, LgdFunctions, 0); + + lua_pushliteral(L, "VERSION"); + lua_pushstring(L, LIB_VERSION); + lua_settable(L, -3); + + lua_pushliteral(L, "COPYRIGHT"); + lua_pushstring(L, LIB_COPYRIGHT); + lua_settable(L, -3); + + tblseticons(L, "MAX_COLORS", gdMaxColors); + tblseticons(L, "GD2_FMT_RAW", GD2_FMT_RAW); + tblseticons(L, "GD2_FMT_COMPRESSED", GD2_FMT_COMPRESSED); + tblseticons(L, "ARC", gdArc); + tblseticons(L, "CHORD", gdChord); + tblseticons(L, "PIE", gdPie); + tblseticons(L, "NO_FILL", gdNoFill); + tblseticons(L, "EDGED", gdEdged); + tblseticons(L, "ANTI_ALIASED", gdAntiAliased); + tblseticons(L, "BRUSHED", gdBrushed); + tblseticons(L, "STYLED", gdStyled); + tblseticons(L, "STYLED_BRUSHED", gdStyledBrushed); + tblseticons(L, "TILED", gdTiled); + tblseticons(L, "TRANSPARENT", gdTransparent); + +#ifdef GD_FREETYPE + /* For gd.StringFTEx */ + tblseticons(L, "FTEX_Unicode", gdFTEX_Unicode); + tblseticons(L, "FTEX_Shift_JIS", gdFTEX_Shift_JIS); + tblseticons(L, "FTEX_Big5", gdFTEX_Big5); +#endif + +#ifdef GD_GIF + /* For gif animation */ + tblseticons(L, "DISPOSAL_NONE", gdDisposalNone); + tblseticons(L, "DISPOSAL_UNKNOWN", gdDisposalUnknown); + tblseticons(L, "DISPOSAL_RESTORE_BACKGROUND", gdDisposalRestoreBackground); + tblseticons(L, "DISPOSAL_RESTORE_PREVIOUS", gdDisposalRestorePrevious); +#endif + + /* Standard gd fonts */ + tblseticons(L, "FONT_TINY", MY_GD_FONT_TINY); + tblseticons(L, "FONT_SMALL", MY_GD_FONT_SMALL); + tblseticons(L, "FONT_MEDIUM", MY_GD_FONT_MEDIUM_BOLD); + tblseticons(L, "FONT_LARGE", MY_GD_FONT_LARGE); + tblseticons(L, "FONT_GIANT", MY_GD_FONT_GIANT); + + luaL_newmetatable(L, GD_IMAGE_PTR_TYPENAME); + lua_pushliteral(L, "__index"); + lua_pushvalue(L, -3); + lua_settable(L, -3); + luaL_setfuncs(L, LgdMetatable, 0); + lua_pop(L, 1); + + return 1; +} + diff --git a/test_features.lua b/test_features.lua new file mode 100644 index 000000000..e6ae14d64 --- /dev/null +++ b/test_features.lua @@ -0,0 +1,24 @@ +#!/usr/bin/env lua + +local gd = require("gd") + +function enabled(res, desc) + local str = " " .. desc .. " " + str = str .. string.rep(".", 37 - string.len(str)) + if res then + print(str .. " Enabled") + else + print(str .. " Disabled") + end +end + +print("Lua-GD version: " .. gd.VERSION) +print("Lua-GD features:") + +enabled(gd.png, "PNG support") +enabled(gd.gif, "GIF support") +enabled(gd.jpeg, "JPEG support") +enabled(gd.createFromXpm, "XPM/XBM support") +enabled(gd.stringFT, "FreeType support") +enabled(gd.useFontConfig, "Fontconfig support") +