diff --git a/core/compress/gzip/example.odin b/core/compress/gzip/example.odin index 0e2c2b9f6..c010d5979 100644 --- a/core/compress/gzip/example.odin +++ b/core/compress/gzip/example.odin @@ -45,7 +45,7 @@ main :: proc() { if len(args) < 2 { stderr("No input file specified.\n") - err := load(slice=TEST, buf=&buf, known_gzip_size=len(TEST)) + err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST)) if err == nil { stdout("Displaying test vector: ") stdout(bytes.buffer_to_string(&buf)) diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 4482d4a7e..4de4d1b63 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -102,7 +102,7 @@ E_Deflate :: compress.Deflate_Error GZIP_MAX_PAYLOAD_SIZE :: i64(max(u32le)) -load :: proc{load_from_slice, load_from_file, load_from_context} +load :: proc{load_from_bytes, load_from_file, load_from_context} load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) { context.allocator = allocator @@ -112,16 +112,16 @@ load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_siz err = E_General.File_Not_Found if ok { - err = load_from_slice(data, buf, len(data), expected_output_size) + err = load_from_bytes(data, buf, len(data), expected_output_size) } return } -load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) { +load_from_bytes :: proc(data: []byte, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) { buf := buf z := &compress.Context_Memory_Input{ - input_data = slice, + input_data = data, output = buf, } return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator) diff --git a/core/image/common.odin b/core/image/common.odin index 4085a0a05..baacd64d9 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -54,9 +54,10 @@ Image :: struct { */ background: Maybe(RGB_Pixel_16), metadata: Image_Metadata, + which: Which_File_Type, } -Image_Metadata :: union { +Image_Metadata :: union #shared_nil { ^Netpbm_Info, ^PNG_Info, ^QOI_Info, @@ -172,6 +173,7 @@ General_Image_Error :: enum { Unable_To_Write_File, // Invalid + Unsupported_Format, Invalid_Signature, Invalid_Input_Image, Image_Dimensions_Too_Large, diff --git a/core/image/general_loader.odin b/core/image/general_loader.odin new file mode 100644 index 000000000..36629c39e --- /dev/null +++ b/core/image/general_loader.odin @@ -0,0 +1,61 @@ +package image + +import "core:mem" +import "core:os" +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 :: proc{ + load_from_bytes, + load_from_file, +} + +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) +} + + +load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + data, ok := os.read_entire_file(filename, allocator) + defer delete(data, allocator) + if ok { + return load_from_bytes(data, options, allocator) + } else { + return nil, .Unable_To_Read_File + } +} + +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) + } +} \ No newline at end of file diff --git a/core/image/netpbm/netpbm.odin b/core/image/netpbm/netpbm.odin index 9574faa26..5a504cd7c 100644 --- a/core/image/netpbm/netpbm.odin +++ b/core/image/netpbm/netpbm.odin @@ -28,7 +28,7 @@ BINARY :: Formats{.P4, .P5, .P6} + PAM + PFM load :: proc { load_from_file, - load_from_buffer, + load_from_bytes, } load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) { @@ -40,13 +40,14 @@ load_from_file :: proc(filename: string, allocator := context.allocator) -> (img return } - return load_from_buffer(data) + return load_from_bytes(data) } -load_from_buffer :: proc(data: []byte, allocator := context.allocator) -> (img: ^Image, err: Error) { +load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (img: ^Image, err: Error) { context.allocator = allocator img = new(Image) + img.which = .NetPBM header: Header; defer header_destroy(&header) header_size: int @@ -748,4 +749,15 @@ autoselect_pbm_format_from_image :: proc(img: ^Image, prefer_binary := true, for // We couldn't find a suitable format return {}, false +} + +@(init, private) +_register :: proc() { + loader :: proc(data: []byte, options: image.Options, allocator: mem.Allocator) -> (img: ^Image, err: Error) { + return load_from_bytes(data, allocator) + } + destroyer :: proc(img: ^Image) { + _ = destroy(img) + } + image.register(.NetPBM, loader, destroyer) } \ No newline at end of file diff --git a/core/image/png/png.odin b/core/image/png/png.odin index d526dfb27..35fdb58d8 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -18,7 +18,6 @@ import "core:compress/zlib" import "core:image" import "core:os" -import "core:strings" import "core:hash" import "core:bytes" import "core:io" @@ -318,13 +317,12 @@ read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) { } chunk_type_to_name :: proc(type: ^image.PNG_Chunk_Type) -> string { - t := transmute(^u8)type - return strings.string_from_ptr(t, 4) + return string(([^]u8)(type)[:4]) } -load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { +load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { ctx := &compress.Context_Memory_Input{ - input_data = slice, + input_data = data, } /* @@ -344,10 +342,9 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont defer delete(data) if ok { - return load_from_slice(data, options) + return load_from_bytes(data, options) } else { - img = new(Image) - return img, .Unable_To_Read_File + return nil, .Unable_To_Read_File } } @@ -375,6 +372,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a if img == nil { img = new(Image) } + img.which = .PNG info := new(image.PNG_Info) img.metadata = info @@ -1639,4 +1637,10 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH return nil } -load :: proc{load_from_file, load_from_slice, load_from_context} +load :: proc{load_from_file, load_from_bytes, load_from_context} + + +@(init, private) +_register :: proc() { + image.register(.PNG, load_from_bytes, destroy) +} \ No newline at end of file diff --git a/core/image/qoi/qoi.odin b/core/image/qoi/qoi.odin index 83b212be8..29a17d4f4 100644 --- a/core/image/qoi/qoi.odin +++ b/core/image/qoi/qoi.odin @@ -180,9 +180,9 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato save :: proc{save_to_memory, save_to_file} -load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { +load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { ctx := &compress.Context_Memory_Input{ - input_data = slice, + input_data = data, } img, err = load_from_context(ctx, options, allocator) @@ -196,10 +196,9 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont defer delete(data) if ok { - return load_from_slice(data, options) + return load_from_bytes(data, options) } else { - img = new(Image) - return img, .Unable_To_Read_File + return nil, .Unable_To_Read_File } } @@ -225,6 +224,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a if img == nil { img = new(Image) } + img.which = .QOI if .return_metadata in options { info := new(image.QOI_Info) @@ -359,7 +359,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a return } -load :: proc{load_from_file, load_from_slice, load_from_context} +load :: proc{load_from_file, load_from_bytes, load_from_context} /* Cleanup of image-specific data. @@ -403,4 +403,9 @@ qoi_hash :: #force_inline proc(pixel: RGBA_Pixel) -> (index: u8) { i4 := u16(pixel.a) * 11 return u8((i1 + i2 + i3 + i4) & 63) +} + +@(init, private) +_register :: proc() { + image.register(.QOI, load_from_bytes, destroy) } \ No newline at end of file diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin index 0539706b3..67a088eb5 100644 --- a/core/image/tga/tga.odin +++ b/core/image/tga/tga.odin @@ -12,7 +12,6 @@ package tga import "core:mem" import "core:image" -import "core:compress" import "core:bytes" import "core:os" diff --git a/core/image/which.odin b/core/image/which.odin index 30cb78405..ab608174f 100644 --- a/core/image/which.odin +++ b/core/image/which.odin @@ -6,6 +6,7 @@ Which_File_Type :: enum { Unknown, BMP, + DjVu, // AT&T DjVu file format EXR, FLIF, GIF, @@ -14,7 +15,7 @@ Which_File_Type :: enum { JPEG, JPEG_2000, JPEG_XL, - PBM, PGM, PPM, PAM, PFM, // NetPBM family + NetPBM, // NetPBM family PIC, // Softimage PIC PNG, // Portable Network Graphics PSD, // Photoshop PSD @@ -88,6 +89,11 @@ which_bytes :: proc(data: []byte) -> Which_File_Type { 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": @@ -111,16 +117,16 @@ which_bytes :: proc(data: []byte) -> Which_File_Type { switch s[2] { case '\t', '\n', '\r': switch s[1] { - case '1', '4': - return .PBM - case '2', '5': - return .PGM - case '3', '6': - return .PPM - case '7': - return .PAM - case 'F', 'f': - return .PFM + 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": diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 36acf7714..36326b48e 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -62,8 +62,10 @@ import fmt "core:fmt" import hash "core:hash" import image "core:image" +import netpbm "core:image/netpbm" import png "core:image/png" import qoi "core:image/qoi" +import tga "core:image/tga" import io "core:io" import log "core:log" @@ -164,8 +166,10 @@ _ :: xml _ :: fmt _ :: hash _ :: image +_ :: netpbm _ :: png _ :: qoi +_ :: tga _ :: io _ :: log _ :: math diff --git a/src/checker.cpp b/src/checker.cpp index d186163e4..da9a97622 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4356,6 +4356,9 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { } String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false); + if (is_blank_ident(import_name)) { + force_use = true; + } // NOTE(bill, 2019-05-19): If the directory path is not a valid entity name, force the user to assign a custom one // if (import_name.len == 0 || import_name == "_") { @@ -4363,17 +4366,13 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { // } if (import_name.len == 0 || is_blank_ident(import_name)) { - if (id->is_using) { - // TODO(bill): Should this be a warning? - } else { - if (id->import_name.string == "") { - String invalid_name = id->fullpath; - invalid_name = get_invalid_import_name(invalid_name); + if (id->import_name.string == "") { + String invalid_name = id->fullpath; + invalid_name = get_invalid_import_name(invalid_name); - error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import \"%.*s\" ", LIT(invalid_name), LIT(invalid_name)); - } else { - error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); - } + error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import \"%.*s\" ", LIT(invalid_name), LIT(invalid_name)); + } else { + error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); } } else { GB_ASSERT(id->import_name.pos.line != 0); @@ -4383,38 +4382,11 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { scope); add_entity(ctx, parent_scope, nullptr, e); - if (force_use || id->is_using) { + if (force_use) { add_entity_use(ctx, nullptr, e); } } - if (id->is_using) { - if (parent_scope->flags & ScopeFlag_Global) { - error(id->import_name, "built-in package imports cannot use using"); - return; - } - - // NOTE(bill): Add imported entities to this file's scope - for_array(elem_index, scope->elements.entries) { - String name = scope->elements.entries[elem_index].key.string; - Entity *e = scope->elements.entries[elem_index].value; - if (e->scope == parent_scope) continue; - - if (is_entity_exported(e, true)) { - Entity *found = scope_lookup_current(parent_scope, name); - if (found != nullptr) { - // NOTE(bill): - // Date: 2019-03-17 - // The order has to be the other way around as `using` adds the entity into the that - // file scope otherwise the error would be the wrong way around - redeclaration_error(name, found, e); - } else { - add_entity_with_name(ctx, parent_scope, e->identifier, e, name); - } - } - } - } - scope->flags |= ScopeFlag_HasBeenImported; } diff --git a/src/parser.cpp b/src/parser.cpp index df7f908a6..1f4093e5f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1160,11 +1160,10 @@ Ast *ast_package_decl(AstFile *f, Token token, Token name, CommentGroup *docs, C return result; } -Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name, +Ast *ast_import_decl(AstFile *f, Token token, Token relpath, Token import_name, CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ImportDecl); result->ImportDecl.token = token; - result->ImportDecl.is_using = is_using; result->ImportDecl.relpath = relpath; result->ImportDecl.import_name = import_name; result->ImportDecl.docs = docs; @@ -4382,7 +4381,6 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { CommentGroup *docs = f->lead_comment; Token token = expect_token(f, Token_import); Token import_name = {}; - bool is_using = kind != ImportDecl_Standard; switch (f->curr_token.kind) { case Token_Ident: @@ -4393,22 +4391,18 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { break; } - if (!is_using && is_blank_ident(import_name)) { - syntax_error(import_name, "Illegal import name: '_'"); - } - Token file_path = expect_token_after(f, Token_String, "import"); Ast *s = nullptr; if (f->curr_proc != nullptr) { - syntax_error(import_name, "You cannot use 'import' within a procedure. This must be done at the file scope"); + syntax_error(import_name, "Cannot use 'import' within a procedure. This must be done at the file scope"); s = ast_bad_decl(f, import_name, file_path); } else { - s = ast_import_decl(f, token, is_using, file_path, import_name, docs, f->line_comment); + s = ast_import_decl(f, token, file_path, import_name, docs, f->line_comment); array_add(&f->imports, s); } - if (is_using) { + if (kind != ImportDecl_Standard) { syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly"); } diff --git a/src/parser.hpp b/src/parser.hpp index c7b4fd0d8..698ed7623 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -585,7 +585,6 @@ AST_KIND(_DeclBegin, "", bool) \ Token import_name; \ CommentGroup *docs; \ CommentGroup *comment; \ - bool is_using; \ }) \ AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \ Token token; \