| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- package image
- import "core:mem"
- import "core:bytes"
- Loader_Proc :: #type proc(data: []byte, options: Options, allocator: mem.Allocator) -> (img: ^Image, err: Error)
- Destroy_Proc :: #type proc(img: ^Image)
- @(private)
- _internal_loaders: [Which_File_Type]Loader_Proc
- _internal_destroyers: [Which_File_Type]Destroy_Proc
- register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) {
- assert(loader != nil)
- assert(destroyer != nil)
- assert(_internal_loaders[kind] == nil)
- _internal_loaders[kind] = loader
- assert(_internal_destroyers[kind] == nil)
- _internal_destroyers[kind] = destroyer
- }
- load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
- loader := _internal_loaders[which(data)]
- if loader == nil {
- return nil, .Unsupported_Format
- }
- return loader(data, options, allocator)
- }
- destroy :: proc(img: ^Image, allocator := context.allocator) {
- if img == nil {
- return
- }
- context.allocator = allocator
- destroyer := _internal_destroyers[img.which]
- if destroyer != nil {
- destroyer(img)
- } else {
- assert(img.metadata == nil)
- bytes.buffer_destroy(&img.pixels)
- free(img)
- }
- }
- Which_File_Type :: enum {
- 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
- }
- which_bytes :: proc(data: []byte) -> Which_File_Type {
- test_tga :: proc(s: string) -> bool {
- get8 :: #force_inline proc(s: ^string) -> u8 {
- v := s[0]
- s^ = s[1:]
- return v
- }
- get16le :: #force_inline proc(s: ^string) -> u16 {
- v := u16(s[0]) | u16(s[1])<<16
- s^ = s[2:]
- return v
- }
- s := s
- s = s[1:] // skip offset
- color_type := get8(&s)
- if color_type > 1 {
- return false
- }
- image_type := get8(&s) // image type
- if color_type == 1 { // Colormap (Paletted) Image
- if image_type != 1 && image_type != 9 { // color type requires 1 or 9
- return false
- }
- s = s[4:] // skip index of first colormap
- bpcme := get8(&s) // check bits per colormap entry
- if bpcme != 8 && bpcme != 15 && bpcme != 16 && bpcme != 24 && bpcme != 32 {
- return false
- }
- s = s[4:] // skip image origin (x, y)
- } else { // Normal image without colormap
- if image_type != 2 && image_type != 3 && image_type != 10 && image_type != 11 {
- return false
- }
- s = s[9:] // skip colormap specification
- }
- if get16le(&s) < 1 || get16le(&s) < 1 { // test width and height
- return false
- }
- bpp := get8(&s) // bits per pixel
- if color_type == 1 && bpp != 8 && bpp != 16 {
- return false
- }
- if bpp != 8 && bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32 {
- return false
- }
- return true
- }
- header: [128]byte
- copy(header[:], data)
- s := string(header[:])
- switch {
- case s[:2] == "BM":
- return .BMP
- case s[:8] == "AT&TFORM":
- switch s[12:16] {
- case "DJVU", "DJVM":
- return .DjVu
- }
- case s[:4] == "\x76\x2f\x31\x01":
- return .EXR
- case s[:6] == "GIF87a", s[:6] == "GIF89a":
- return .GIF
- case s[6:10] == "JFIF", s[6:10] == "Exif":
- return .JPEG
- case s[:3] == "\xff\xd8\xff":
- switch s[4] {
- case 0xdb, 0xee, 0xe1, 0xe0:
- return .JPEG
- }
- switch {
- case s[:12] == "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01":
- return .JPEG
- }
- case s[:4] == "\xff\x4f\xff\x51", s[:12] == "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a":
- return .JPEG_2000
- case s[:12] == "\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a":
- return .JPEG_XL
- case s[0] == 'P':
- switch s[2] {
- case '\t', '\n', '\r':
- switch s[1] {
- case '1', '4': // PBM
- return .NetPBM
- case '2', '5': // PGM
- return .NetPBM
- case '3', '6': // PPM
- return .NetPBM
- case '7': // PAM
- return .NetPBM
- case 'F', 'f': // PFM
- return .NetPBM
- }
- }
- case s[:8] == "\x89PNG\r\n\x1a\n":
- return .PNG
- case s[:4] == "qoif":
- return .QOI
- case s[:2] == "\x01\xda":
- return .SGI_RGB
- case s[:4] == "\x59\xA6\x6A\x95":
- return .Sun_Rast
- case s[:4] == "MM\x2a\x00", s[:4] == "II\x00\x2A":
- return .TIFF
- case s[:4] == "RIFF" && s[8:12] == "WEBP":
- return .WebP
- case s[:8] == "#define ":
- return .XBM
- case s[:11] == "#?RADIANCE\n", s[:7] == "#?RGBE\n":
- return .HDR
- case s[:4] == "\x38\x42\x50\x53":
- return .PSD
- case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT":
- return .PIC
- case s[:4] == "\x69\x63\x6e\x73":
- return .ICNS
- case s[:4] == "\x46\x4c\x49\x46":
- return .FLIF
- case:
- // More complex formats
- if test_tga(s) {
- return .TGA
- }
- }
- return .Unknown
- }
|