package core:image

Overview

package image implements a general 2D image library to be used with other image related packages

Types

Alpha_Key ¶

Alpha_Key :: union {
	[2]u8, 
	[4]u8, 
	[2]u16, 
	[4]u16, 
}

Channel ¶

Channel :: enum u8 {
	R = 1, 
	G = 2, 
	B = 3, 
	A = 4, 
}

Destroy_Proc ¶

Destroy_Proc :: proc "odin" (img: ^Image)

Error ¶

Error :: union {
	General_Image_Error, 
	Netpbm_Error, 
	PNG_Error, 
	QOI_Error, 
	compress.Error, 
	compress.General_Error, 
	compress.Deflate_Error, 
	compress.ZLIB_Error, 
	runtime.Allocator_Error, 
}

GA_Pixel ¶

GA_Pixel :: [2]u8

GA_Pixel_16 ¶

GA_Pixel_16 :: [2]u16

G_Pixel ¶

G_Pixel :: [1]u8
 

Grayscale

G_Pixel_16 ¶

G_Pixel_16 :: [1]u16

General_Image_Error ¶

General_Image_Error :: enum int {
	None                          = 0, 
	Unsupported_Option, 
	// File I/O
	Unable_To_Read_File, 
	Unable_To_Write_File, 
	// Invalid
	Unsupported_Format, 
	Invalid_Signature, 
	Invalid_Input_Image, 
	Image_Dimensions_Too_Large, 
	Invalid_Image_Dimensions, 
	Invalid_Number_Of_Channels, 
	Image_Does_Not_Adhere_to_Spec, 
	Invalid_Image_Depth, 
	Invalid_Bit_Depth, 
	Invalid_Color_Space, 
	// More data than pixels to decode into, for example.
	Corrupt, 
	// Output buffer is the wrong size
	Invalid_Output, 
	// Allocation
	Unable_To_Allocate_Or_Resize, 
}

Image ¶

Image :: struct {
	width:      int,
	height:     int,
	channels:   int,
	depth:      int,
	// Channel depth in bits, typically 8 or 16
	pixels:     bytes.Buffer "fmt:\"-\"",
	// 		Some image loaders/writers can return/take an optional background color.
	// 		For convenience, we return them as u16 so we don't need to switch on the type
	// 		in our viewer, and can just test against nil.
	background: runtime.Maybe(RGB_Pixel_16),
	metadata:   Image_Metadata,
	which:      Which_File_Type,
}

Image_Metadata ¶

Image_Metadata :: union {
	^Netpbm_Info, 
	^PNG_Info, 
	^QOI_Info, 
	^TGA_Info, 
}

Loader_Proc ¶

Loader_Proc :: proc "odin" (data: []u8, options: Options, allocator: runtime.Allocator) -> (img: ^Image, err: Error)

Netpbm_Error ¶

Netpbm_Error :: enum int {
	None                           = 0, 
	// reading
	Invalid_Header_Token_Character, 
	Incomplete_Header, 
	Invalid_Header_Value, 
	Duplicate_Header_Field, 
	Buffer_Too_Small, 
	Invalid_Buffer_ASCII_Token, 
	Invalid_Buffer_Value, 
	// writing
	Invalid_Format, 
}

Netpbm_Format ¶

Netpbm_Format :: enum int {
	P1, 
	P2, 
	P3, 
	P4, 
	P5, 
	P6, 
	P7, 
	Pf, 
	PF, 
}
 

Netpbm-specific definitions

Netpbm_Header ¶

Netpbm_Header :: struct {
	format:        Netpbm_Format,
	width:         int,
	height:        int,
	channels:      int,
	depth:         int,
	maxval:        int,
	tupltype:      string,
	scale:         f32,
	little_endian: bool,
}

Netpbm_Info ¶

Netpbm_Info :: struct {
	header: Netpbm_Header,
}

Option ¶

Option :: enum int {
	// LOAD OPTIONS
	info                    = 0, 
	do_not_decompress_image, 
	return_header, 
	return_metadata, 
	alpha_add_if_missing,        // Ignored for QOI. Always returns RGBA8.
	alpha_drop_if_present,       // Unimplemented for QOI. Returns error.
	alpha_premultiply,           // Unimplemented for QOI. Returns error.
	blend_background,            // Ignored for non-PNG formats
	// Unimplemented
	do_not_expand_grayscale, 
	do_not_expand_indexed, 
	do_not_expand_channels, 
	// SAVE OPTIONS
	qoi_all_channels_linear,     // QOI, informative only. If not set, defaults to sRGB with linear alpha.
}

Options ¶

Options :: distinct bit_set[Option]

PNG_Chunk ¶

PNG_Chunk :: struct #packed {
	header: PNG_Chunk_Header,
	data:   []u8,
	crc:    u32be,
}

PNG_Chunk_Header ¶

PNG_Chunk_Header :: struct #packed {
	length: u32be,
	type:   PNG_Chunk_Type,
}

PNG_Chunk_Type ¶

PNG_Chunk_Type :: enum u32be {
	// IHDR must come first in a file
	IHDR = 1229472850, 
	// PLTE must precede the first IDAT chunk
	PLTE = 1347179589, 
	bKGD = 1649100612, 
	tRNS = 1951551059, 
	IDAT = 1229209940, 
	iTXt = 1767135348, 
	tEXt = 1950701684, 
	zTXt = 2052348020, 
	iCCP = 1766015824, 
	pHYs = 1883789683, 
	gAMA = 1732332865, 
	tIME = 1950960965, 
	sPLT = 1934642260, 
	sRGB = 1934772034, 
	hIST = 1749635924, 
	cHRM = 1665684045, 
	sBIT = 1933723988, 
	// 		eXIf tags are not part of the core spec, but have been ratified
	// 		in v1.5.0 of the PNG Ext register.
	// 
	// 		We will provide unprocessed chunks to the caller if `.return_metadata` is set.
	// 		Applications are free to implement an Exif decoder.
	eXIf = 1700284774, 
	// PNG files must end with IEND
	IEND = 1229278788, 
	// 		XCode sometimes produces "PNG" files that don't adhere to the PNG spec.
	// 		We recognize them only in order to avoid doing further work on them.
	// 
	// 		Some tools like PNG Defry may be able to repair them, but we're not
	// 		going to reward Apple for producing proprietary broken files purporting
	// 		to be PNGs by supporting them.
	iDOT = 1766084436, 
	CgBI = 1130840649, 
}

PNG_Color_Type ¶

PNG_Color_Type :: distinct bit_set[PNG_Color_Value; u8]

PNG_Color_Value ¶

PNG_Color_Value :: enum u8 {
	Paletted = 0, // 1 << 0 = 1
	Color    = 1, // 1 << 1 = 2
	Alpha    = 2, // 1 << 2 = 4
}

PNG_Error ¶

PNG_Error :: enum int {
	None                          = 0, 
	IHDR_Not_First_Chunk, 
	IHDR_Corrupt, 
	IDAT_Missing, 
	IDAT_Must_Be_Contiguous, 
	IDAT_Corrupt, 
	IDAT_Size_Too_Large, 
	PLTE_Encountered_Unexpectedly, 
	PLTE_Invalid_Length, 
	PLTE_Missing, 
	TRNS_Encountered_Unexpectedly, 
	TNRS_Invalid_Length, 
	BKGD_Invalid_Length, 
	Unknown_Color_Type, 
	Invalid_Color_Bit_Depth_Combo, 
	Unknown_Filter_Method, 
	Unknown_Interlace_Method, 
	Requested_Channel_Not_Present, 
	Post_Processing_Error, 
	Invalid_Chunk_Length, 
}
 

PNG-specific definitions

PNG_IHDR ¶

PNG_IHDR :: struct #packed {
	width:              u32be,
	height:             u32be,
	bit_depth:          u8,
	color_type:         PNG_Color_Type,
	compression_method: u8,
	filter_method:      u8,
	interlace_method:   PNG_Interlace_Method,
}

PNG_Info ¶

PNG_Info :: struct {
	header: PNG_IHDR,
	chunks: [dynamic]PNG_Chunk,
}

PNG_Interlace_Method ¶

PNG_Interlace_Method :: enum u8 {
	None  = 0, 
	Adam7 = 1, 
}

QOI_Color_Space ¶

QOI_Color_Space :: enum u8 {
	sRGB   = 0, 
	Linear = 1, 
}

QOI_Error ¶

QOI_Error :: enum int {
	None                       = 0, 
	Missing_Or_Corrupt_Trailer,     // Image seemed to have decoded okay, but trailer is missing or corrupt.
}
 

QOI-specific definitions

QOI_Header ¶

QOI_Header :: struct #packed {
	magic:       u32be,
	width:       u32be,
	height:      u32be,
	channels:    u8,
	color_space: QOI_Color_Space,
}

QOI_Info ¶

QOI_Info :: struct {
	header: QOI_Header,
}

RGBA_Pixel ¶

RGBA_Pixel :: [4]u8

RGBA_Pixel_16 ¶

RGBA_Pixel_16 :: [4]u16

RGB_Pixel ¶

RGB_Pixel :: [3]u8
 

Color

RGB_Pixel_16 ¶

RGB_Pixel_16 :: [3]u16

TGA_Alpha_Kind ¶

TGA_Alpha_Kind :: enum u8 {
	None, 
	Undefined_Ignore, 
	Undefined_Retain, 
	Useful, 
	Premultiplied, 
}

TGA_Data_Type ¶

TGA_Data_Type :: enum u8 {
	No_Image_Data             = 0, 
	Uncompressed_Color_Mapped = 1, 
	Uncompressed_RGB          = 2, 
	Uncompressed_Black_White  = 3, 
	Compressed_Color_Mapped   = 9, 
	Compressed_RGB            = 10, 
	Compressed_Black_White    = 11, 
}

TGA_Extension ¶

TGA_Extension :: struct #packed {
	extension_size:          u16le,
	// Size of this struct. If not 495 bytes it means it's an unsupported version.
	author_name:             [41]u8 "fmt:\"s,0\"",
	// Author name, ASCII. Zero-terminated
	author_comments:         [324]u8 "fmt:\"s,0\"",
	// Author comments, formatted as 4 lines of 80 character lines, each zero terminated.
	datetime:                struct {
		month,
		day,
		year,
		hour,
		minute,
		second: u16le,
	},
	job_name:                [41]u8 "fmt:\"s,0\"",
	// Author name, ASCII. Zero-terminated
	job_time:                struct {
		hour,
		minute,
		second: u16le,
	},
	software_id:             [41]u8 "fmt:\"s,0\"",
	// Software ID name, ASCII. Zero-terminated
	software_version:        struct #packed {
		number: u16le,
		// Version number * 100
		letter: u8 "fmt:\"r\"",
	},
	key_color:               [4]u8,
	// ARGB key color used at time of production
	aspect_ratio:            [2]u16le,
	// Numerator / Denominator
	gamma:                   [2]u16le,
	// Numerator / Denominator, range should be 0.0..10.0
	color_correction_offset: u32le,
	// 0 if no color correction information
	postage_stamp_offset:    u32le,
	// 0 if no thumbnail
	scanline_offset:         u32le,
	// 0 if no scanline table
	attributes:              TGA_Alpha_Kind,
}
TGA_Footer :: struct #packed {
	extension_area_offset:      u32le,
	developer_directory_offset: u32le,
	signature:                  [18]u8 "fmt:\"s,0\"",
}

TGA_Header ¶

TGA_Header :: struct #packed {
	id_length:        u8,
	color_map_type:   u8,
	data_type_code:   TGA_Data_Type,
	color_map_origin: u16le,
	color_map_length: u16le,
	color_map_depth:  u8,
	origin:           [2]u16le,
	dimensions:       [2]u16le,
	bits_per_pixel:   u8,
	image_descriptor: u8,
}

TGA_Info ¶

TGA_Info :: struct {
	header:    TGA_Header,
	image_id:  string,
	footer:    runtime.Maybe(TGA_Footer),
	extension: runtime.Maybe(TGA_Extension),
}

Which_File_Type ¶

Which_File_Type :: enum int {
	Unknown, 
	BMP, 
	DjVu,      // AT&T DjVu file format
	EXR, 
	FLIF, 
	GIF, 
	HDR,       // Radiance RGBE HDR
	ICNS,      // Apple Icon Image
	JPEG, 
	JPEG_2000, 
	JPEG_XL, 
	NetPBM,    // NetPBM family
	PIC,       // Softimage PIC
	PNG,       // Portable Network Graphics
	PSD,       // Photoshop PSD
	QOI,       // Quite Okay Image
	SGI_RGB,   // Silicon Graphics Image RGB file format
	Sun_Rast,  // Sun Raster Graphic
	TGA,       // Targa Truevision
	TIFF,      // Tagged Image File Format
	WebP, 
	XBM,       // X BitMap
}

Constants

MAX_DIMENSIONS ¶

MAX_DIMENSIONS :: min(#config(MAX_DIMENSIONS, 8192 * 8192), 65535 * 65535)
 

67_108_864 pixels max by default.

For QOI, the Worst case scenario means all pixels will be encoded as RGBA literals, costing 5 bytes each.
This caps memory usage at 320 MiB.

The tunable is limited to 4_294_836_225 pixels maximum, or 4 GiB per 8-bit channel.
It is not advised to tune it this large.

The 64 Megapixel default is considered to be a decent upper bound you won't run into in practice,
except in very specific circumstances.

New_TGA_Signature ¶

New_TGA_Signature :: "TRUEVISION-XFILE.\x00"

PNG_IHDR_SIZE ¶

PNG_IHDR_SIZE :: size_of(PNG_IHDR)

QOI_Magic ¶

QOI_Magic :: u32be(0x716f6966)

Variables

This section is empty.

Procedures

alpha_add_if_missing ¶

alpha_add_if_missing :: proc "odin" (img: ^Image, alpha_key: Alpha_Key = Alpha_Key{}, allocator := context.allocator) -> (ok: bool) {…}
 

Add alpha channel if missing, in-place.

Expects 1..4 channels (Gray, Gray + Alpha, RGB, RGBA).
Any other number of channels will be considered an error, returning `false` without modifying the image.
If the input image already has an alpha channel, it'll return `true` early (without considering optional keyed alpha).

If an image doesn't already have an alpha channel:
If the optional `alpha_key` is provided, it will be resolved as follows:
	- For RGB,  if pix = key.rgb -> pix = {0, 0, 0, key.a}
	- For Gray, if pix = key.r  -> pix = {0, key.g}
Otherwise, an opaque alpha channel will be added.

alpha_apply_keyed_alpha ¶

alpha_apply_keyed_alpha :: alpha_add_if_missing
 

Add alpha channel if missing, in-place.

Expects 1..4 channels (Gray, Gray + Alpha, RGB, RGBA).
Any other number of channels will be considered an error, returning `false` without modifying the image.
If the input image already has an alpha channel, it'll return `true` early (without considering optional keyed alpha).

If an image doesn't already have an alpha channel:
If the optional `alpha_key` is provided, it will be resolved as follows:
	- For RGB,  if pix = key.rgb -> pix = {0, 0, 0, key.a}
	- For Gray, if pix = key.r  -> pix = {0, key.g}
Otherwise, an opaque alpha channel will be added.

alpha_drop_if_present ¶

alpha_drop_if_present :: proc "odin" (img: ^Image, options: Options = Options{}, alpha_key: Alpha_Key = Alpha_Key{}, allocator := context.allocator) -> (ok: bool) {…}
 

Drop alpha channel if present, in-place.

Expects 1..4 channels (Gray, Gray + Alpha, RGB, RGBA).
Any other number of channels will be considered an error, returning `false` without modifying the image.

Of the `options`, the following are considered:
`.alpha_premultiply`
	If the image has an alpha channel, returns image data as follows:
		RGB *= A, Gray = Gray *= A

`.blend_background`
	If `img.background` is set, it'll be blended in like this:
		RGB = (1 - A) * Background + A * RGB

If an image has 1 (Gray) or 3 (RGB) channels, it'll return early without modifying the image,
with one exception: `alpha_key` and `img.background` are present, and `.blend_background` is set.

In this case a keyed alpha pixel will be replaced with the background color.

apply_palette_rgb ¶

apply_palette_rgb :: proc "odin" (img: ^Image, palette: [256][3]u8, allocator := context.allocator) -> (ok: bool) {…}
 

Apply palette to 8-bit single-channel image and return an 8-bit RGB image, in-place. If the image given is not a valid 8-bit single channel image, the procedure will return false early.

apply_palette_rgba ¶

apply_palette_rgba :: proc "odin" (img: ^Image, palette: [256][4]u8, allocator := context.allocator) -> (ok: bool) {…}
 

Apply palette to 8-bit single-channel image and return an 8-bit RGBA image, in-place. If the image given is not a valid 8-bit single channel image, the procedure will return false early.

compute_buffer_size ¶

compute_buffer_size :: proc "odin" (width, height, channels, depth: int, extra_row_bytes: int = int(0)) -> (size: int) {…}
 

Function to help with image buffer calculations

destroy ¶

destroy :: proc "odin" (img: ^Image, allocator := context.allocator) {…}

expand_grayscale ¶

expand_grayscale :: proc "odin" (img: ^Image, allocator := context.allocator) -> (ok: bool) {…}
 

Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate. Returns early with false if already an RGB(A) image.

is_valid_color_image ¶

is_valid_color_image :: proc "odin" (img: ^Image) -> (ok: bool) {…}
 

Does the image have 3 or 4 channels, a valid bit depth (8 or 16), Is the pointer valid, are the dimensions valid?

is_valid_grayscale_image ¶

is_valid_grayscale_image :: proc "odin" (img: ^Image) -> (ok: bool) {…}
 

Does the image have 1 or 2 channels, a valid bit depth (8 or 16), Is the pointer valid, are the dimensions valid?

is_valid_image ¶

is_valid_image :: proc "odin" (img: ^Image) -> (ok: bool) {…}
 

Does the image have 1..4 channels, a valid bit depth (8 or 16), Is the pointer valid, are the dimensions valid?

load_from_bytes ¶

load_from_bytes :: proc "odin" (data: []u8, options: Options, allocator: runtime.Allocator) -> (img: ^Image, err: Error) {…}

load_from_file ¶

load_from_file :: proc "odin" (filename: string, options: Options = Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {…}

read_data ¶

read_data :: proc "odin" (z: $, $T: typeid) -> (res: typeid, err: compress.General_Error) {…}

read_u8 ¶

read_u8 :: proc "odin" (z: $) -> (res: u8, err: compress.General_Error) {…}

register ¶

register :: proc "odin" (kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) {…}

return_single_channel ¶

return_single_channel :: proc "odin" (img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {…}
 

When you have an RGB(A) image, but want a particular channel.

which_bytes ¶

which_bytes :: proc "odin" (data: []u8) -> Which_File_Type {…}

which_file ¶

which_file :: proc "odin" (path: string) -> Which_File_Type {…}

write_bytes ¶

write_bytes :: proc "odin" (buf: ^bytes.Buffer, data: []u8) -> (err: compress.General_Error) {…}

Procedure Groups

Source Files

Generation Information

Generated with odin version dev-2022-10 (vendor "odin") Windows_amd64 @ 2022-10-05 21:11:47.336171000 +0000 UTC