tagline |
---|
in-memory bitmaps |
- multiple pixel formats, color spaces, channel layouts, scanline orderings,
row strides, and bit depths.
- arbitrary row strides, including sub-byte strides.
- top-down and bottom-up scanline order.
- conversion between most formats.
- reading and writing pixel data in a uniform way, independent of the pixel format.
- dithering, pixel effects, filters.
- fast (see benchmarks).
- only packed formats, no separate plane formats
- but: custom conversions to gray8 and gray16 can be used to separate the channels of any format into separate bitmaps.
- only expanded formats, no palette formats
- but: custom formats with a custom reader and writer can be easily made to use a palette which itself can be a one-row bitmap.
- no conversions to cmyk (would need color profiling)
- no conversions to ycc and ycck
A bitmap is a table format, i.e. any table with the following fields is a bitmap:
w
,h
- bitmap dimensions, in pixels.stride
- row stride in bytes. must be at leastw * bpp / 8
(can be fractional for < 8bpp formats).bottom_up
- iftrue
, the rows are are arranged bottom-up instead of top-down.data
- the pixel buffer (string or a cdata buffer). the pixels must be packed instride
-long rows, top-down or bottom-up.size
- size of the pixel buffer, in bytes.format
- the pixel format, either a string naming a predefined format (below table), or a table specifying a custom format (see customization).
name colortype channels bits/channel bits/pixel
rgb8, bgr8 rgba8 RGB 8 24 rgb16, bgr16 rgba16 RGB 16 48 rgbx8, bgrx8, xrgb8, xbgr8 rgba8 RGB 8 32 rgbx16, bgrx16, xrgb16, xbgr16 rgba16 RGB 16 64 rgba8, bgra8, argb8, abgr8 rgba8 RGB+alpha 8 32 rgba16, bgra16, argb16, abgr16 rgba16 RGB+alpha 16 64 rgb565 rgba8 RGB 5/6/5 16 rgb0555 rgba8 RGB 5 16 rgb5550 rgba8 RGB 5 16 rgb444 rgba8 RGB 4 16 rgba4444 rgba8 RGB+alpha 4 16 rgba5551 rgba8 RGB+alpha 5/5/5/1 16 rgba1555 rgba8 RGB+alpha 1/5/5/5 16 ga8, ag8 ga8 GRAY+alpha 8 16 ga16, ag16 ga16 GRAY+alpha 16 32 g1 ga8 GRAY 1 1 g2 ga8 GRAY 2 2 g4 ga8 GRAY 4 4 g8 ga8 GRAY 8 8 g16 ga16 GRAY 16 16 cmyk8 cmyk8 inverse CMYK 8 32 ycc8 ycc8 JPEG YCbCr 8 8 24 ycck8 ycck8 JPEG YCbCrK 8 8 32 rgbaf rgbaf RGB+alpha 32 128 rgbad rgbaf RGB+alpha 64 256 raw8 raw8 X 8 8 raw16 raw16 X 16 16 raw32 raw32 X 32 32 raw64 raw64 X 64 64
NOTE: For 16-bit RGB formats the color channels are stored in a little-endian unsigned integer, so rgb565 actually contains the blue and half of the green channel in its first byte.
name channels value type value range
rgba8 r, g, b, a integer 0..0xff rgba16 r, g, b, a integer 0..0xffff ga8 g, a integer 0..0xff ga16 g, a integer 0..0xffff cmyk8 c, m, y, k integer 0..0xff ycc8 y, c, c integer 0..0xff ycck8 y, c, c, k integer 0..0xff rgbaf r, g, b, a float or double 0..1 raw8 x integer 0..0xff raw16 x integer 0..0xffff raw32 x integer 0..0xffffffff raw64 x integer 0..0xffffffffffffffffUL
bitmap info
bitmap.format(bmp|format_name) -> format
, bitmap format (a table)
bitmap.stride(bmp) -> stride
row stride in bytes
bitmap.row_size(bmp) -> size
row size in bytes
bitmap.colortype(bmp|colortype_name) -> colortype
bitmap colortype (a table)
bitmap operations
bitmap.new(w, h, ...) -> dst
create a bitmap
bitmap.copy(src[, format], ...) -> dst
copy and convert a bitmap
bitmap.paint(dst, src, dstx, dsty, ...) -> dst
paint a bitmap on another
bitmap.clear([byte_value])
clear bitmap
bitmap.sub(src, [x], [y], [w], [h]) -> dst
make a sub-bitmap
pixel access
bitmap.pixel_interface(src) -> getpixel, setpixel
get a pixel interface
bitmap.channel_interface(bmp, n) -> getval, setval
get a channel interface
dithering
bitmap.dither.fs(bmp, rN, gN, bN, aN)
apply dithering
bitmap.dither.ordered(bmp, rN, gN, bN, aN)
apply dithering
effects
bitmap.invert(bmp)
invert colors (in place)
bitmap.grayscale(bmp)
desaturate (in place)
bitmap.convolve(src, kernel, [edge]) -> dst
convolve
bitmap.sharpen(src[, threshold]) -> dst
sharpen
bitmap.mirror(src)
mirror horizontally (in place)
alpha blending
bitmap.blend(src, dst, [operator], [x], [y])
blend source into dest bitmap
resizing
bitmap.resize.nearest|bilinear(src, w, h) -> dst
resize to new
bitmap.resize.nearest|bilinear(src, dst) -> dst
resize to dest
utilities
bitmap.min_stride(format, width) -> min_stride
minimum stride for width
bitmap.aligned_stride(stride[, align]) -> stride, align
next aligned stride
bitmap.aligned_pointer(ptr[, align]) -> ptr, align
next aligned pointer
Create a bitmap object. The optional align
(which defaults to 1) specifies
the data pointer and stride alignment (true
means 4). The optional alloc
is an alloc(bytes) -> data
function (eg. [glue].malloc).
Copy a bitmap, optionally to a new format, orientation and stride. If format
is not specified, stride and orientation default to those of source bitmap's,
otherwise they default to top-down, minimum stride.
bitmap.paint(dest_bmp, source_bmp[, dstx, dsty][, convert_pixel, [src_colortype], [dst_colortype]]) -> dest_bmp
Paint a source bitmap into a destination bitmap, with all the necessary clipping and pixel and colortype conversions.
The optional convert_pixel
is a pixel conversion function to be called for
each pixel as convert_pixel(a, b, c, ...) -> x, y, z, ...
. It receives
the channel values of the source bitmap in its original colortype
(or in src_colortype
, if given) and must return the converted channel
values for the destination bitmap in its colortype (or in dst_colortype
,
if that is given).
In some cases, the destination bitmap is allowed to have the same data buffer
as the source bitmap. Specifically, it must have the same orientation,
smaller or equal stride and smaller or equal pixel size. The destination
bitmap can also be the source bitmap itself, which is useful for performing
custom transformations via the convert_pixel
callback.
Crop a bitmap without copying the pixels (the data
field of the sub-bitmap
is a pointer into the data
buffer of the parent bitmap). The parent bitmap
is pinned in the parent
field of the sub-bitmap to prevent garbage
collection of the data buffer. Other than that, the sub-bitmap behaves exactly
like a normal bitmap (it can be further sub'ed for instance). The coordinates
default to 0, 0, bmp.w, bmp.h
respectively. The coordinates are adjusted
to fit the parent bitmap. If they result in zero width or height,
nothing is returned.
To get real cropping, just copy the bitmap, specifying the format and orientation to reset the stride:
sub = bitmap.copy(sub, sub.format, sub.bottom_up)
NOTE: For 1, 2, 4 bpp formats, the coordinates must be such that the data pointer points to the beginning of a byte (that is, is not fractional). For a non-fractional stride, this means the
x
coordinate must be a multiple of 8, 4, 2 respectively. For fractional strides don't even bother.
Return an API for getting and setting individual pixels of a bitmap object:
getpixel(x, y) -> a, b, c, ...
setpixel(x, y, a, b, c, ...)
where a, b, c are the individual color channels, converted to the specified colortype or in the colortype of the bitmap (i.e. r, g, b, a for the 'rgba' colortype, etc.).
local function darken(r, g, b, a)
return r / 2, g / 2, b / 2, a --make 2x darker
end
local getpixel, setpixel = bitmap.pixel_interface(bmp)
for y = 0, bmp.h-1 do
for x = 0, bmp.w-1 do
setpixel(x, y, darken(getpixel(x, y)))
end
end
--the above has the same effect as:
bitmap.paint(bmp, bmp, darken)
Return an API for getting and setting values for a single color channel:
getvalue(x, y) -> v
setvalue(x, y, v)
Dither a bitmap using the Floyd-Steinberg dithering algorithm. *bits
specify the number of bits of color to keep for each channel (eg.
bitmap.dither.fs(bmp, 5, 6, 5, 0)
dithers a bitmap so that its colors fit
into the rgb565
format). Only implemented for 4-channel colortypes.
Dither a bitmap using the ordered dithering algorithm. mapsize
specifies
the threshold map to use and can be 2, 3, 4 or 8. Use the demo to see how
this parameter affects the output quality depending on the output format
(it's not a clear-cut choice). Implemented for 2-channel and 4-channel
colortypes. Note that actual clipping of the low bits is not done, it
will be done naturally when converting the bitmap to a lower bit depth.
Invert colors.
Convert pixels to grayscale, without changing the format.
Convolve a bitmap using a kernel matrix (a Lua array of arrays of the same
length). edge
can be crop
, wrap
or extend
(default is extend
).
Sharpen a bitmap.
Blend source_bmp
into dest_bmp
using a blending operator at x,y
coordinates in the target bitmap (default is 0,0
).
Operators are in the bitmap.blend_op
table for inspection.
bitmap.resize.nearest|bilinear(bmp, w, h) -> new_bmp
bitmap.resize.nearest|bilinear(source_bmp, dest_bmp) -> dest_bmp
Resize a bitmap.
Return the minimum stride in bytes given a format and width.
A bitmap data buffer should never be smaller than min_stride * height
.
Given a stride (which can also be fractional) and a power-of-two alignment,
return the next smallest stride that is a multiple of the alignment
(align
defaults to 1 and true
means 4).
Same as aligned_stride()
but for pointers. The returned pointer is of type
void*
.
Bitmap's row size, in bytes, i.e. bitmap's minimum stride.
Given a source bitmap format, iterate through all the formats that the source
format can be converted to. name
is the format name and def
is
the format definition which is a table with the fields bpp
, ctype
,
colortype
, read
, write
.
Print the list of supported pixel formats and the list of supported colortype conversions.
Extending the bitmap
module with new colortypes, formats, conversions
and module functions is easy. Look at the bitmap_rgbaf
sub-module for
an example on how to do that. For the submodule to be loaded automatically
you need to reference it in the bitmap
module too in a few key spots.
Again, look at how rgbaf
does it.
A custom pixel format definition is a table with the following fields:
bpp
- pixel size, in bits (must be an even number of bits).ctype
- C type to castdata
to when reading and writing pixels (see below).colortype
- a string naming a standard color type or a table specifying a custom color type. The channel values thatread
andwrite
refer to depend on the colortype, eg. for the 'rgba8' colortype, the read function must return 4 numbers in the 0-255 range corresponding to the R, G, B, A channels.read
- a function to be called asread(data, i) -> a, b, c, ...
; the function must decode the pixel atdata[i]
and return its channel values according to colortype.write
- a function to be called aswrite(data, i, a, b, c, ...)
; the function must encode the given channel values according to colortype and write the pixel atdata[i]
.- for formats that have bpp < 8, the index i is fractional and the bit
offset of the pixel is at
bit.band(i * 8, 7)
.
- for formats that have bpp < 8, the index i is fractional and the bit
offset of the pixel is at
A custom colortype definition is a table with the following fields:
channels
- a string with each letter a channel name, eg. 'rgba', so that#channels
indicates the number of channels.max
- maximum value to which the channel values need to be clipped.bpc
- bits/channel - same meaning asmax
but in bits.
- premuliply / unpremultiply alpha