mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
examples: add an image viewer program (#12797)
This commit is contained in:
parent
f0969698e2
commit
d3b769d1bc
21
examples/viewer/LICENSE
Normal file
21
examples/viewer/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Dario Deledda
|
||||
|
||||
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
|
||||
AUTHORS OR COPYRIGHT HOLDERS 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.
|
54
examples/viewer/README.md
Normal file
54
examples/viewer/README.md
Normal file
@ -0,0 +1,54 @@
|
||||
# vviewer
|
||||
Image viewer for V
|
||||
|
||||
This is an example of a simple image viewer written in V.
|
||||
|
||||
## Usage
|
||||
|
||||
The program can be invoked by the command line:
|
||||
|
||||
file list: `viewer img1.jpg inmg2.bmp img3.tga`
|
||||
folder list: `viewer folder1 folder2`
|
||||
zip list: `viewer folder1.zip folder2.zip`
|
||||
|
||||
All folders/zips are scanned for images.
|
||||
The user can mix files, folders, and zips.
|
||||
|
||||
mixed list: `viewer img1.jpg img2.bmp folder1 folder2 img2.tga folder1.zip`
|
||||
|
||||
## Interactive usage
|
||||
|
||||
Run the viewer then drag and drop files,folders and zips on it.
|
||||
|
||||
## Accepted image format
|
||||
|
||||
JPEG, PNG, BMP, PSD, TGA, GIF (not animated), HDR, PIC, PNM
|
||||
|
||||
#### Functions
|
||||
The user can navigate through the files passed to the viewer.
|
||||
The following operations can be performed on each image:
|
||||
|
||||
- **Pan**, move over the image
|
||||
- **Zoom**, magnify or reduce the image
|
||||
- **Rotate**, rotate by 90 degree steps
|
||||
|
||||
## Key bindings
|
||||
|
||||
**H** - show this help
|
||||
|
||||
**ESC/q** - Quit
|
||||
**cursor right** - Next image
|
||||
**cursor left** - Previous image
|
||||
**cursor up** - Next folder
|
||||
**cursor down** - Previous folder
|
||||
**F** - Toggle full screen
|
||||
**R** - Rotate image of 90 degree
|
||||
**I** - Toggle the info text
|
||||
|
||||
**mouse wheel** - next/previous images
|
||||
Hold **left Mouse button** - Pan on the image
|
||||
Hold **right Mouse button** - Zoom on the image
|
||||
|
||||
#### Author:
|
||||
|
||||
Dario Deledda 2021 (c)
|
343
examples/viewer/file_scan.v
Normal file
343
examples/viewer/file_scan.v
Normal file
@ -0,0 +1,343 @@
|
||||
/**********************************************************************
|
||||
*
|
||||
* File scanner
|
||||
*
|
||||
* Copyright (c) 2021 Dario Deledda. All rights reserved.
|
||||
* Use of this source code is governed by an MIT license
|
||||
* that can be found in the LICENSE file.
|
||||
*
|
||||
* TODO:
|
||||
**********************************************************************/
|
||||
import os
|
||||
|
||||
// STBI supported format
|
||||
// STBI_NO_JPEG *
|
||||
// STBI_NO_PNG *
|
||||
// STBI_NO_BMP *
|
||||
// STBI_NO_PSD
|
||||
// STBI_NO_TGA *
|
||||
// STBI_NO_GIF *
|
||||
// STBI_NO_HDR *
|
||||
// STBI_NO_PIC *
|
||||
// STBI_NO_PNM *
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Struct and Enums
|
||||
*
|
||||
******************************************************************************/
|
||||
enum Item_type {
|
||||
file = 0
|
||||
folder
|
||||
// archive format
|
||||
zip = 16
|
||||
archive_file
|
||||
// graphic format, MUST stay after the other types!!
|
||||
bmp = 32
|
||||
jpg
|
||||
png
|
||||
gif
|
||||
tga
|
||||
ppm
|
||||
pgm
|
||||
pic
|
||||
hdr
|
||||
}
|
||||
|
||||
pub struct Item {
|
||||
pub mut:
|
||||
path string
|
||||
name string
|
||||
size u64
|
||||
i_type Item_type = .file
|
||||
container_index int // used if the item is in a container (.zip, .rar, etc)
|
||||
container_item_index int // index in the container if the item is contained
|
||||
need_extract bool // if true need to extraction from the container
|
||||
drawable bool // if true the image can be showed
|
||||
n_item int //
|
||||
rotation int // number of rotation of PI/2
|
||||
}
|
||||
|
||||
struct Item_list {
|
||||
pub mut:
|
||||
lst []Item
|
||||
path_sep string
|
||||
item_index int = -1 // image currently shown
|
||||
n_item int // number of images scanned
|
||||
loaded bool // flag that indicate that the list is ready to be used
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Utility functions
|
||||
*
|
||||
******************************************************************************/
|
||||
[inline]
|
||||
fn modulo(x int, n int) int {
|
||||
return (x % n + n) % n
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn get_extension(x string) Item_type {
|
||||
// 4 char extension check
|
||||
if x.len > 4 {
|
||||
ext4 := x[x.len - 4..].to_lower()
|
||||
match ext4 {
|
||||
// containers
|
||||
'.zip' { return .zip }
|
||||
// graphic formats
|
||||
'.jpg' { return .jpg }
|
||||
'.png' { return .png }
|
||||
'.bmp' { return .bmp }
|
||||
'.gif' { return .gif }
|
||||
'.tga' { return .tga }
|
||||
'.ppm' { return .ppm }
|
||||
'.pgm' { return .pgm }
|
||||
'.pic' { return .pic }
|
||||
'.hdr' { return .hdr }
|
||||
else {}
|
||||
}
|
||||
}
|
||||
// 5 char extension check
|
||||
if x.len > 5 {
|
||||
ext5 := x[x.len - 5..].to_lower()
|
||||
if ext5 == '.jpeg' {
|
||||
{
|
||||
return .jpg
|
||||
}
|
||||
}
|
||||
}
|
||||
return .file
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn is_image(x Item_type) bool {
|
||||
if int(x) >= int(Item_type.bmp) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn is_container(x Item_type) bool {
|
||||
if x in [.zip, .folder] {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn (item_list Item_list) is_inside_a_container() bool {
|
||||
if item_list.lst.len <= 0 || item_list.n_item <= 0 {
|
||||
return false
|
||||
}
|
||||
return item_list.lst[item_list.item_index].need_extract
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn (item_list Item_list) get_file_path() string {
|
||||
if item_list.lst.len <= 0 || item_list.n_item <= 0 {
|
||||
return ''
|
||||
}
|
||||
if item_list.lst[item_list.item_index].path.len > 0 {
|
||||
return '${item_list.lst[item_list.item_index].path}$item_list.path_sep${item_list.lst[item_list.item_index].name}'
|
||||
}
|
||||
return item_list.lst[item_list.item_index].name
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Scan functions
|
||||
*
|
||||
******************************************************************************/
|
||||
fn (mut item_list Item_list) scan_folder(path string, in_index int) ? {
|
||||
println('Scanning [$path]')
|
||||
mut folder_list := []string{}
|
||||
lst := os.ls(path) ?
|
||||
|
||||
// manage the single files
|
||||
for c, x in lst {
|
||||
pt := '$path$item_list.path_sep$x'
|
||||
mut item := Item{
|
||||
path: path
|
||||
name: x
|
||||
container_index: in_index
|
||||
container_item_index: c
|
||||
}
|
||||
if os.is_dir(pt) {
|
||||
folder_list << x
|
||||
} else {
|
||||
ext := get_extension(x)
|
||||
if ext == .zip {
|
||||
item.i_type = .zip
|
||||
item_list.lst << item
|
||||
item_list.scan_zip(pt, item_list.lst.len - 1) ?
|
||||
continue
|
||||
}
|
||||
if is_image(ext) == true {
|
||||
item_list.n_item += 1
|
||||
item.n_item = item_list.n_item
|
||||
item.i_type = ext
|
||||
item.drawable = true
|
||||
item_list.lst << item
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// manage the folders
|
||||
for x in folder_list {
|
||||
pt := '$path$item_list.path_sep$x'
|
||||
item := Item{
|
||||
path: path
|
||||
name: x
|
||||
i_type: .folder
|
||||
}
|
||||
item_list.lst << item
|
||||
item_list.scan_folder(pt, item_list.lst.len - 1) ?
|
||||
}
|
||||
// println(item_list.lst.len)
|
||||
// println("==================================")
|
||||
}
|
||||
|
||||
fn (item_list Item_list) print_list() {
|
||||
println('================================')
|
||||
for x in item_list.lst {
|
||||
if x.i_type == .folder {
|
||||
print('[]')
|
||||
}
|
||||
if x.i_type == .zip {
|
||||
print('[ZIP]')
|
||||
}
|
||||
println('$x.path => $x.container_index $x.container_item_index $x.name ne:$x.need_extract')
|
||||
}
|
||||
println('n_item: $item_list.n_item index: $item_list.item_index')
|
||||
println('================================')
|
||||
}
|
||||
|
||||
fn (mut item_list Item_list) get_items_list(args []string) {
|
||||
item_list.loaded = false
|
||||
println('Args: $args')
|
||||
|
||||
item_list.path_sep = $if windows { '\\' } $else { '/' }
|
||||
for x in args {
|
||||
// scan folder
|
||||
if os.is_dir(x) {
|
||||
mut item := Item{
|
||||
path: x
|
||||
name: x
|
||||
container_index: item_list.lst.len
|
||||
i_type: .folder
|
||||
}
|
||||
item_list.lst << item
|
||||
item_list.scan_folder(x, item_list.lst.len - 1) or {
|
||||
eprintln('ERROR: scanning folder [$x]!')
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
mut item := Item{
|
||||
path: ''
|
||||
name: x
|
||||
container_index: -1
|
||||
}
|
||||
ext := get_extension(x)
|
||||
// scan .zip
|
||||
if ext == .zip {
|
||||
item.i_type = .zip
|
||||
item_list.lst << item
|
||||
item_list.scan_zip(x, item_list.lst.len - 1) or {
|
||||
eprintln('ERROR: scanning zip [$x]!')
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
// single images
|
||||
if is_image(ext) == true {
|
||||
item_list.n_item += 1
|
||||
item.n_item = item_list.n_item
|
||||
item.i_type = ext
|
||||
item.drawable = true
|
||||
item_list.lst << item
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
// debug call for list all the loaded items
|
||||
// item_list.print_list()
|
||||
|
||||
println('Items: $item_list.n_item')
|
||||
println('Scanning done.')
|
||||
|
||||
item_list.get_next_item(1)
|
||||
item_list.loaded = true
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Navigation functions
|
||||
*
|
||||
******************************************************************************/
|
||||
fn (mut item_list Item_list) get_next_item(in_inc int) {
|
||||
// if empty exit
|
||||
if item_list.lst.len <= 0 || item_list.n_item <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
inc := if in_inc > 0 { 1 } else { -1 }
|
||||
mut i := item_list.item_index + in_inc
|
||||
i = modulo(i, item_list.lst.len)
|
||||
start := i
|
||||
for {
|
||||
if item_list.lst[i].drawable == true {
|
||||
item_list.item_index = i
|
||||
break
|
||||
}
|
||||
i = i + inc
|
||||
i = modulo(i, item_list.lst.len)
|
||||
// if we are in a loop break it
|
||||
if i == start {
|
||||
break
|
||||
}
|
||||
}
|
||||
// println("Found: ${item_list.item_index}")
|
||||
}
|
||||
|
||||
fn (mut item_list Item_list) go_to_next_container(in_inc int) {
|
||||
// if empty exit
|
||||
if item_list.lst.len <= 0 || item_list.n_item <= 0 {
|
||||
return
|
||||
}
|
||||
inc := if in_inc > 0 { 1 } else { -1 }
|
||||
mut i := item_list.item_index + in_inc
|
||||
i = modulo(i, item_list.lst.len)
|
||||
start := i
|
||||
for {
|
||||
// check if we found a folder
|
||||
if is_container(item_list.lst[i].i_type) == true
|
||||
&& i != item_list.lst[item_list.item_index].container_index {
|
||||
item_list.item_index = i
|
||||
item_list.get_next_item(1)
|
||||
break
|
||||
}
|
||||
// continue to search
|
||||
i = i + inc
|
||||
i = modulo(i, item_list.lst.len)
|
||||
// if we are in a loop break it
|
||||
if i == start {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Other functions
|
||||
*
|
||||
******************************************************************************/
|
||||
[inline]
|
||||
fn (mut item_list Item_list) rotate(in_inc int) {
|
||||
item_list.lst[item_list.item_index].rotation += in_inc
|
||||
if item_list.lst[item_list.item_index].rotation >= 4 {
|
||||
item_list.lst[item_list.item_index].rotation = 0
|
||||
}
|
||||
}
|
7
examples/viewer/v.mod
Normal file
7
examples/viewer/v.mod
Normal file
@ -0,0 +1,7 @@
|
||||
Module {
|
||||
name: 'vviewer',
|
||||
description: 'A simple image viewer written in V.',
|
||||
version: '0.9',
|
||||
repo_url: 'https://github.com/vlang/v/tree/master/examples/viewer',
|
||||
dependencies: []
|
||||
}
|
832
examples/viewer/view.v
Normal file
832
examples/viewer/view.v
Normal file
@ -0,0 +1,832 @@
|
||||
/**********************************************************************
|
||||
*
|
||||
* simple Picture Viewer V. 0.9
|
||||
*
|
||||
* Copyright (c) 2021 Dario Deledda. All rights reserved.
|
||||
* Use of this source code is governed by an MIT license
|
||||
* that can be found in the LICENSE file.
|
||||
*
|
||||
* TODO:
|
||||
* - add an example with shaders
|
||||
**********************************************************************/
|
||||
import os
|
||||
import gg
|
||||
import gx
|
||||
import sokol.gfx
|
||||
import sokol.sgl
|
||||
import sokol.sapp
|
||||
import stbi
|
||||
import szip
|
||||
import strings
|
||||
|
||||
// Help text
|
||||
const (
|
||||
help_text_rows = [
|
||||
'Image Viwer 0.9 help.',
|
||||
'',
|
||||
'ESC/q - Quit',
|
||||
'cur. right - Next image',
|
||||
'cur. left - Previous image',
|
||||
'cur. up - Next folder',
|
||||
'cur. down - Previous folder',
|
||||
'F - Toggle full screen',
|
||||
'R - Rotate image of 90 degree',
|
||||
'I - Toggle the info text',
|
||||
'',
|
||||
'mouse wheel - next/previous images',
|
||||
'keep pressed left Mouse button - Pan on the image',
|
||||
'keep pressed rigth Mouse button - Zoom on the image',
|
||||
]
|
||||
)
|
||||
|
||||
const (
|
||||
win_width = 800
|
||||
win_height = 800
|
||||
bg_color = gx.black
|
||||
pi_2 = 3.14159265359 / 2.0
|
||||
uv = [f32(0), 0, 1, 0, 1, 1, 0, 1]! // used for zoom icon during rotations
|
||||
|
||||
text_drop_files = 'Drop here some images/folder/zip to navigate in the pics'
|
||||
text_scanning = 'Scanning...'
|
||||
text_loading = 'Loading...'
|
||||
)
|
||||
|
||||
enum Viewer_state {
|
||||
loading
|
||||
scanning
|
||||
show
|
||||
error
|
||||
}
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
gg &gg.Context
|
||||
pip_viewer C.sgl_pipeline
|
||||
texture C.sg_image
|
||||
init_flag bool
|
||||
frame_count int
|
||||
mouse_x int = -1
|
||||
mouse_y int = -1
|
||||
scroll_y int
|
||||
|
||||
state Viewer_state = .scanning
|
||||
// translation
|
||||
tr_flag bool
|
||||
tr_x f32 = 0.0
|
||||
tr_y f32 = 0.0
|
||||
last_tr_x f32 = 0.0
|
||||
last_tr_y f32 = 0.0
|
||||
// scaling
|
||||
sc_flag bool
|
||||
scale f32 = 1.0
|
||||
sc_x f32 = 0.0
|
||||
sc_y f32 = 0.0
|
||||
last_sc_x f32 = 0.0
|
||||
last_sc_y f32 = 0.0
|
||||
// loaded image
|
||||
img_w int
|
||||
img_h int
|
||||
img_ratio f32 = 1.0
|
||||
// item list
|
||||
item_list &Item_list
|
||||
// Text info and help
|
||||
show_info_flag bool = true
|
||||
show_help_flag bool
|
||||
// zip container
|
||||
zip &szip.Zip // pointer to the szip structure
|
||||
zip_index int = -1 // index of the zip contaire item
|
||||
// memory buffer
|
||||
mem_buf voidptr // buffer used to load items from files/containers
|
||||
mem_buf_size int // size of the buffer
|
||||
// font
|
||||
font_path string // path to the temp font file
|
||||
// logo
|
||||
logo_path string // path of the temp font logo
|
||||
logo_texture C.sg_image
|
||||
logo_w int
|
||||
logo_h int
|
||||
logo_ratio f32 = 1.0
|
||||
// string builder
|
||||
bl strings.Builder = strings.new_builder(512)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Texture functions
|
||||
*
|
||||
******************************************************************************/
|
||||
fn create_texture(w int, h int, buf &u8) C.sg_image {
|
||||
sz := w * h * 4
|
||||
mut img_desc := C.sg_image_desc{
|
||||
width: w
|
||||
height: h
|
||||
num_mipmaps: 0
|
||||
min_filter: .linear
|
||||
mag_filter: .linear
|
||||
// usage: .dynamic
|
||||
wrap_u: .clamp_to_edge
|
||||
wrap_v: .clamp_to_edge
|
||||
label: &byte(0)
|
||||
d3d11_texture: 0
|
||||
}
|
||||
// comment if .dynamic is enabled
|
||||
img_desc.data.subimage[0][0] = C.sg_range{
|
||||
ptr: buf
|
||||
size: usize(sz)
|
||||
}
|
||||
|
||||
sg_img := C.sg_make_image(&img_desc)
|
||||
return sg_img
|
||||
}
|
||||
|
||||
fn destroy_texture(sg_img C.sg_image) {
|
||||
C.sg_destroy_image(sg_img)
|
||||
}
|
||||
|
||||
// Use only if: .dynamic is enabled
|
||||
fn update_text_texture(sg_img C.sg_image, w int, h int, buf &byte) {
|
||||
sz := w * h * 4
|
||||
mut tmp_sbc := C.sg_image_data{}
|
||||
tmp_sbc.subimage[0][0] = C.sg_range{
|
||||
ptr: buf
|
||||
size: usize(sz)
|
||||
}
|
||||
C.sg_update_image(sg_img, &tmp_sbc)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Memory buffer
|
||||
*
|
||||
******************************************************************************/
|
||||
[inline]
|
||||
fn (mut app App) resize_buf_if_needed(in_size int) {
|
||||
// manage the memory buffer
|
||||
if app.mem_buf_size < in_size {
|
||||
println('Managing FILE memory buffer, allocated [$in_size]Bytes')
|
||||
// free previous buffer if any exist
|
||||
if app.mem_buf_size > 0 {
|
||||
unsafe {
|
||||
free(app.mem_buf)
|
||||
}
|
||||
}
|
||||
// allocate the memory
|
||||
unsafe {
|
||||
app.mem_buf = malloc(int(in_size))
|
||||
app.mem_buf_size = int(in_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Loading functions
|
||||
*
|
||||
******************************************************************************/
|
||||
// read_bytes from file in `path` in the memory buffer of app.
|
||||
[manualfree]
|
||||
fn (mut app App) read_bytes(path string) bool {
|
||||
mut fp := os.vfopen(path, 'rb') or {
|
||||
eprintln('ERROR: Can not open the file [$path].')
|
||||
return false
|
||||
}
|
||||
defer {
|
||||
C.fclose(fp)
|
||||
}
|
||||
cseek := C.fseek(fp, 0, C.SEEK_END)
|
||||
if cseek != 0 {
|
||||
eprintln('ERROR: Can not seek in the file [$path].')
|
||||
return false
|
||||
}
|
||||
fsize := C.ftell(fp)
|
||||
if fsize < 0 {
|
||||
eprintln('ERROR: File [$path] has size is 0.')
|
||||
return false
|
||||
}
|
||||
C.rewind(fp)
|
||||
|
||||
app.resize_buf_if_needed(int(fsize))
|
||||
|
||||
nr_read_elements := int(C.fread(app.mem_buf, fsize, 1, fp))
|
||||
if nr_read_elements == 0 && fsize > 0 {
|
||||
eprintln('ERROR: Can not read the file [$path] in the memory buffer.')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// read a file as []byte
|
||||
pub fn read_bytes_from_file(file_path string) []byte {
|
||||
mut buffer := []byte{}
|
||||
buffer = os.read_bytes(file_path) or {
|
||||
eprintln('ERROR: Texure file: [$file_path] NOT FOUND.')
|
||||
exit(0)
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
fn (mut app App) load_texture_from_buffer(buf voidptr, buf_len int) (C.sg_image, int, int) {
|
||||
// load image
|
||||
stbi.set_flip_vertically_on_load(true)
|
||||
img := stbi.load_from_memory(buf, buf_len) or {
|
||||
eprintln('ERROR: Can not load image from buffer, file: [${app.item_list.lst[app.item_list.item_index]}].')
|
||||
return app.logo_texture, app.logo_w, app.logo_h
|
||||
// exit(1)
|
||||
}
|
||||
res := create_texture(int(img.width), int(img.height), img.data)
|
||||
unsafe {
|
||||
img.free()
|
||||
}
|
||||
return res, int(img.width), int(img.height)
|
||||
}
|
||||
|
||||
pub fn (mut app App) load_texture_from_file(file_name string) (C.sg_image, int, int) {
|
||||
app.read_bytes(file_name)
|
||||
return app.load_texture_from_buffer(app.mem_buf, app.mem_buf_size)
|
||||
}
|
||||
|
||||
pub fn show_logo(mut app App) {
|
||||
clear_modifier_params(mut app)
|
||||
if app.texture != app.logo_texture {
|
||||
destroy_texture(app.texture)
|
||||
}
|
||||
app.texture = app.logo_texture
|
||||
app.img_w = app.logo_w
|
||||
app.img_h = app.logo_h
|
||||
app.img_ratio = f32(app.img_w) / f32(app.img_h)
|
||||
// app.gg.refresh_ui()
|
||||
}
|
||||
|
||||
pub fn load_image(mut app App) {
|
||||
if app.item_list.loaded == false || app.init_flag == false {
|
||||
// show_logo(mut app)
|
||||
// app.state = .show
|
||||
return
|
||||
}
|
||||
app.state = .loading
|
||||
clear_modifier_params(mut app)
|
||||
// destroy the texture, avoid to destroy the logo
|
||||
if app.texture != app.logo_texture {
|
||||
destroy_texture(app.texture)
|
||||
}
|
||||
|
||||
// load from .ZIP file
|
||||
if app.item_list.is_inside_a_container() == true {
|
||||
app.texture, app.img_w, app.img_h = app.load_texture_from_zip() or {
|
||||
eprintln('ERROR: Can not load image from .ZIP file [${app.item_list.lst[app.item_list.item_index]}].')
|
||||
show_logo(mut app)
|
||||
app.state = .show
|
||||
return
|
||||
}
|
||||
app.img_ratio = f32(app.img_w) / f32(app.img_h)
|
||||
app.state = .show
|
||||
// app.gg.refresh_ui()
|
||||
return
|
||||
}
|
||||
|
||||
// if we are out of the zip, close it
|
||||
if app.zip_index >= 0 {
|
||||
app.zip_index = -1
|
||||
app.zip.close()
|
||||
}
|
||||
|
||||
file_path := app.item_list.get_file_path()
|
||||
if file_path.len > 0 {
|
||||
// println("${app.item_list.lst[app.item_list.item_index]} $file_path ${app.item_list.lst.len}")
|
||||
app.texture, app.img_w, app.img_h = app.load_texture_from_file(file_path)
|
||||
app.img_ratio = f32(app.img_w) / f32(app.img_h)
|
||||
// println("texture: [${app.img_w},${app.img_h}] ratio: ${app.img_ratio}")
|
||||
} else {
|
||||
app.texture = app.logo_texture
|
||||
app.img_w = app.logo_w
|
||||
app.img_h = app.logo_h
|
||||
app.img_ratio = f32(app.img_w) / f32(app.img_h)
|
||||
println('texture NOT FOUND: use logo!')
|
||||
}
|
||||
app.state = .show
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Init / Cleanup
|
||||
*
|
||||
******************************************************************************/
|
||||
fn app_init(mut app App) {
|
||||
app.init_flag = true
|
||||
|
||||
// 3d pipeline
|
||||
mut pipdesc := C.sg_pipeline_desc{}
|
||||
unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) }
|
||||
|
||||
color_state := C.sg_color_state{
|
||||
blend: C.sg_blend_state{
|
||||
enabled: true
|
||||
src_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_SRC_ALPHA)
|
||||
dst_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA)
|
||||
}
|
||||
}
|
||||
pipdesc.colors[0] = color_state
|
||||
|
||||
pipdesc.depth = C.sg_depth_state{
|
||||
write_enabled: true
|
||||
compare: gfx.CompareFunc(C.SG_COMPAREFUNC_LESS_EQUAL)
|
||||
}
|
||||
pipdesc.cull_mode = .back
|
||||
app.pip_viewer = sgl.make_pipeline(&pipdesc)
|
||||
|
||||
// load logo
|
||||
app.logo_texture, app.logo_w, app.logo_h = app.load_texture_from_file(app.logo_path)
|
||||
app.logo_ratio = f32(app.img_w) / f32(app.img_h)
|
||||
|
||||
app.img_w = app.logo_w
|
||||
app.img_h = app.logo_h
|
||||
app.img_ratio = app.logo_ratio
|
||||
app.texture = app.logo_texture
|
||||
|
||||
println('INIT DONE!')
|
||||
|
||||
// init done, load the first image if any
|
||||
load_image(mut app)
|
||||
}
|
||||
|
||||
fn cleanup(mut app App) {
|
||||
gfx.shutdown()
|
||||
|
||||
// delete temp files
|
||||
os.rm(app.font_path) or { eprintln('ERROR: Can not delete temp font file.') }
|
||||
os.rm(app.logo_path) or { eprintln('ERROR: Can not delete temp logo file.') }
|
||||
println('Cleaning done.')
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Draw functions
|
||||
*
|
||||
******************************************************************************/
|
||||
[manualfree]
|
||||
fn frame(mut app App) {
|
||||
ws := gg.window_size_real_pixels()
|
||||
if ws.width <= 0 || ws.height <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
mut ratio := f32(ws.width) / ws.height
|
||||
dw := ws.width
|
||||
dh := ws.height
|
||||
|
||||
app.gg.begin()
|
||||
sgl.defaults()
|
||||
|
||||
// set viewport
|
||||
sgl.viewport(0, 0, dw, dh, true)
|
||||
|
||||
// enable our pipeline
|
||||
sgl.load_pipeline(app.pip_viewer)
|
||||
sgl.enable_texture()
|
||||
sgl.texture(app.texture)
|
||||
|
||||
// translation
|
||||
tr_x := app.tr_x / app.img_w
|
||||
tr_y := -app.tr_y / app.img_h
|
||||
sgl.push_matrix()
|
||||
sgl.translate(tr_x, tr_y, 0.0)
|
||||
// scaling/zoom
|
||||
sgl.scale(2.0 * app.scale, 2.0 * app.scale, 0.0)
|
||||
// roation
|
||||
mut rotation := 0
|
||||
if app.state == .show && app.item_list.n_item > 0 {
|
||||
rotation = app.item_list.lst[app.item_list.item_index].rotation
|
||||
sgl.rotate(pi_2 * f32(rotation), 0.0, 0.0, -1.0)
|
||||
}
|
||||
|
||||
// draw the image
|
||||
mut w := f32(0.5)
|
||||
mut h := f32(0.5)
|
||||
|
||||
// for 90 and 270 degree invert w and h
|
||||
// rotation change image ratio, manage it
|
||||
if rotation & 1 == 1 {
|
||||
tmp := w
|
||||
w = h
|
||||
h = tmp
|
||||
h /= app.img_ratio * ratio
|
||||
} else {
|
||||
h /= app.img_ratio / ratio
|
||||
}
|
||||
|
||||
// manage image overflow in case of strange scales
|
||||
if h > 0.5 {
|
||||
reduction_factor := 0.5 / h
|
||||
h = h * reduction_factor
|
||||
w = w * reduction_factor
|
||||
}
|
||||
if w > 0.5 {
|
||||
reduction_factor := 0.5 / w
|
||||
h = h * reduction_factor
|
||||
w = w * reduction_factor
|
||||
}
|
||||
|
||||
// println("$w,$h")
|
||||
// white multiplicator for now
|
||||
mut c := [byte(255), 255, 255]!
|
||||
sgl.begin_quads()
|
||||
sgl.v2f_t2f_c3b(-w, -h, 0, 0, c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(w, -h, 1, 0, c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(w, h, 1, 1, c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(-w, h, 0, 1, c[0], c[1], c[2])
|
||||
sgl.end()
|
||||
|
||||
// restore all the transformations
|
||||
sgl.pop_matrix()
|
||||
|
||||
// Zoom icon
|
||||
/*
|
||||
if app.show_info_flag == true && app.scale > 1 {
|
||||
mut bw := f32(0.25)
|
||||
mut bh := f32(0.25 / app.img_ratio)
|
||||
|
||||
// manage the rotations
|
||||
if rotation & 1 == 1 {
|
||||
bw,bh = bh,bw
|
||||
}
|
||||
mut bx := f32(1 - bw)
|
||||
mut by := f32(1 - bh)
|
||||
if rotation & 1 == 1 {
|
||||
bx,by = by,bx
|
||||
}
|
||||
|
||||
bh_old1 := bh
|
||||
bh *= ratio
|
||||
by += (bh_old1 - bh)
|
||||
|
||||
// draw the zoom icon
|
||||
sgl.begin_quads()
|
||||
r := int(u32(rotation) << 1)
|
||||
sgl.v2f_t2f_c3b(bx , by , uv[(0 + r) & 7] , uv[(1 + r) & 7], c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(bx + bw, by , uv[(2 + r) & 7] , uv[(3 + r) & 7], c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(bx + bw, by + bh, uv[(4 + r) & 7] , uv[(5 + r) & 7], c[0], c[1], c[2])
|
||||
sgl.v2f_t2f_c3b(bx , by + bh, uv[(6 + r) & 7] , uv[(7 + r) & 7], c[0], c[1], c[2])
|
||||
sgl.end()
|
||||
|
||||
// draw the zoom rectangle
|
||||
sgl.disable_texture()
|
||||
|
||||
bw_old := bw
|
||||
bh_old := bh
|
||||
bw /= app.scale
|
||||
bh /= app.scale
|
||||
bx += (bw_old - bw) / 2 - (tr_x / 8) / app.scale
|
||||
by += (bh_old - bh) / 2 - ((tr_y / 8) / app.scale) * ratio
|
||||
|
||||
c = [byte(255),255,0]! // yellow
|
||||
sgl.begin_line_strip()
|
||||
sgl.v2f_c3b(bx , by , c[0], c[1], c[2])
|
||||
sgl.v2f_c3b(bx + bw, by , c[0], c[1], c[2])
|
||||
sgl.v2f_c3b(bx + bw, by + bh, c[0], c[1], c[2])
|
||||
sgl.v2f_c3b(bx , by + bh, c[0], c[1], c[2])
|
||||
sgl.v2f_c3b(bx , by , c[0], c[1], c[2])
|
||||
sgl.end()
|
||||
}
|
||||
*/
|
||||
sgl.disable_texture()
|
||||
|
||||
//
|
||||
// Draw info text
|
||||
//
|
||||
x := 10
|
||||
y := 10
|
||||
|
||||
app.gg.begin()
|
||||
|
||||
if app.state in [.scanning, .loading] {
|
||||
if app.state == .scanning {
|
||||
draw_text(mut app, text_scanning, x, y, 20)
|
||||
} else {
|
||||
draw_text(mut app, text_loading, x, y, 20)
|
||||
}
|
||||
} else if app.state == .show {
|
||||
// print the info text if needed
|
||||
if app.item_list.n_item > 0 && app.show_info_flag == true {
|
||||
/*
|
||||
// waiting for better autofree
|
||||
num := app.item_list.lst[app.item_list.item_index].n_item
|
||||
of_num := app.item_list.n_item
|
||||
x_screen := int(w*2*app.scale*dw)
|
||||
y_screen := int(h*2*app.scale*dw)
|
||||
rotation_angle := 90 * rotation
|
||||
scale_str := "${app.scale:.2}"
|
||||
text := "${num}/${of_num} [${app.img_w},${app.img_h}]=>[${x_screen},${y_screen}] ${app.item_list.lst[app.item_list.item_index].name} scale: ${scale_str} rotation: ${rotation_angle}"
|
||||
//text := "${num}/${of_num}"
|
||||
draw_text(mut app, text, 10, 10, 20)
|
||||
unsafe{
|
||||
text.free()
|
||||
}
|
||||
*/
|
||||
|
||||
// Using string builder to avoid memory leak
|
||||
num := app.item_list.lst[app.item_list.item_index].n_item
|
||||
of_num := app.item_list.n_item
|
||||
x_screen := int(w * 2 * app.scale * dw)
|
||||
y_screen := int(h * 2 * app.scale * dw)
|
||||
rotation_angle := 90 * rotation
|
||||
scale_str := '${app.scale:.2}'
|
||||
app.bl.clear()
|
||||
app.bl.write_string('$num/$of_num')
|
||||
app.bl.write_string(' [${app.img_w}x$app.img_h]=>[${x_screen}x$y_screen]')
|
||||
app.bl.write_string(' ${app.item_list.lst[app.item_list.item_index].name}')
|
||||
app.bl.write_string(' scale: $scale_str rotation: $rotation_angle')
|
||||
draw_text(mut app, app.bl.str(), 10, 10, 20)
|
||||
} else {
|
||||
if app.item_list.n_item <= 0 {
|
||||
draw_text(mut app, text_drop_files, 10, 10, 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Draw Help text
|
||||
//
|
||||
if app.show_help_flag == true {
|
||||
mut txt_y := 30
|
||||
for r in help_text_rows {
|
||||
draw_text(mut app, r, 10, txt_y, 20)
|
||||
txt_y += 20
|
||||
}
|
||||
}
|
||||
|
||||
app.gg.end()
|
||||
app.frame_count++
|
||||
}
|
||||
|
||||
// draw readable text
|
||||
fn draw_text(mut app App, in_txt string, in_x int, in_y int, fnt_sz f32) {
|
||||
scale := app.gg.scale
|
||||
font_size := int(fnt_sz * scale)
|
||||
|
||||
mut txt_conf_c0 := gx.TextCfg{
|
||||
color: gx.white // gx.rgb( (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff)
|
||||
align: .left
|
||||
size: font_size
|
||||
}
|
||||
mut txt_conf_c1 := gx.TextCfg{
|
||||
color: gx.black // gx.rgb( (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff)
|
||||
align: .left
|
||||
size: font_size
|
||||
}
|
||||
|
||||
x := int(in_x * scale)
|
||||
y := int(in_y * scale)
|
||||
app.gg.draw_text(x + 2, y + 2, in_txt, txt_conf_c0)
|
||||
app.gg.draw_text(x, y, in_txt, txt_conf_c1)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* events management
|
||||
*
|
||||
******************************************************************************/
|
||||
fn clear_modifier_params(mut app App) {
|
||||
app.scale = 1.0
|
||||
|
||||
app.sc_flag = false
|
||||
app.sc_x = 0
|
||||
app.sc_y = 0
|
||||
app.last_sc_x = 0
|
||||
app.last_sc_y = 0
|
||||
|
||||
app.tr_flag = false
|
||||
app.tr_x = 0
|
||||
app.tr_y = 0
|
||||
app.last_tr_x = 0
|
||||
app.last_tr_y = 0
|
||||
}
|
||||
|
||||
fn my_event_manager(mut ev gg.Event, mut app App) {
|
||||
// navigation using the mouse wheel
|
||||
app.scroll_y = int(ev.scroll_y)
|
||||
if app.scroll_y != 0 {
|
||||
inc := int(-1 * app.scroll_y / 4)
|
||||
if app.item_list.n_item > 0 {
|
||||
app.item_list.get_next_item(inc)
|
||||
load_image(mut app)
|
||||
}
|
||||
}
|
||||
|
||||
if ev.typ == .mouse_move {
|
||||
app.mouse_x = int(ev.mouse_x)
|
||||
app.mouse_y = int(ev.mouse_y)
|
||||
}
|
||||
if ev.typ == .touches_began || ev.typ == .touches_moved {
|
||||
if ev.num_touches > 0 {
|
||||
touch_point := ev.touches[0]
|
||||
app.mouse_x = int(touch_point.pos_x)
|
||||
app.mouse_y = int(touch_point.pos_y)
|
||||
}
|
||||
}
|
||||
|
||||
// clear all parameters
|
||||
if ev.typ == .mouse_down && ev.mouse_button == .middle {
|
||||
clear_modifier_params(mut app)
|
||||
}
|
||||
|
||||
// ws := gg.window_size_real_pixels()
|
||||
// ratio := f32(ws.width) / ws.height
|
||||
// dw := ws.width
|
||||
// dh := ws.height
|
||||
|
||||
// --- translate ---
|
||||
if ev.typ == .mouse_down && ev.mouse_button == .left {
|
||||
app.tr_flag = true
|
||||
app.last_tr_x = app.mouse_x
|
||||
app.last_tr_y = app.mouse_y
|
||||
}
|
||||
if ev.typ == .mouse_up && ev.mouse_button == .left && app.tr_flag == true {
|
||||
app.tr_flag = false
|
||||
}
|
||||
if ev.typ == .mouse_move && app.tr_flag == true {
|
||||
app.tr_x += (app.mouse_x - app.last_tr_x) * 3 * app.gg.scale
|
||||
app.tr_y += (app.mouse_y - app.last_tr_y) * 3 * app.gg.scale
|
||||
app.last_tr_x = app.mouse_x
|
||||
app.last_tr_y = app.mouse_y
|
||||
// println("Translate: ${app.tr_x} ${app.tr_y}")
|
||||
}
|
||||
|
||||
// --- scaling ---
|
||||
if ev.typ == .mouse_down && ev.mouse_button == .right && app.sc_flag == false {
|
||||
app.sc_flag = true
|
||||
app.last_sc_x = app.mouse_x
|
||||
app.last_sc_y = app.mouse_y
|
||||
}
|
||||
if ev.typ == .mouse_up && ev.mouse_button == .right && app.sc_flag == true {
|
||||
app.sc_flag = false
|
||||
}
|
||||
if ev.typ == .mouse_move && app.sc_flag == true {
|
||||
app.sc_x = app.mouse_x - app.last_sc_x
|
||||
app.sc_y = app.mouse_y - app.last_sc_y
|
||||
app.last_sc_x = app.mouse_x
|
||||
app.last_sc_y = app.mouse_y
|
||||
|
||||
app.scale += f32(app.sc_x / 100)
|
||||
if app.scale < 0.1 {
|
||||
app.scale = 0.1
|
||||
}
|
||||
if app.scale > 32 {
|
||||
app.scale = 32
|
||||
}
|
||||
}
|
||||
|
||||
if ev.typ == .key_down {
|
||||
// println(ev.key_code)
|
||||
|
||||
// Exit using the ESC key or Q key
|
||||
if ev.key_code == .escape || ev.key_code == .q {
|
||||
cleanup(mut app)
|
||||
exit(0)
|
||||
}
|
||||
// Toggle info text OSD
|
||||
if ev.key_code == .i {
|
||||
app.show_info_flag = !app.show_info_flag
|
||||
}
|
||||
// Toggle help text
|
||||
if ev.key_code == .h {
|
||||
app.show_help_flag = !app.show_help_flag
|
||||
}
|
||||
|
||||
// do actions only if there are items in the list
|
||||
if app.item_list.loaded == true && app.item_list.n_item > 0 {
|
||||
// show previous image
|
||||
if ev.key_code == .left {
|
||||
app.item_list.get_next_item(-1)
|
||||
load_image(mut app)
|
||||
}
|
||||
// show next image
|
||||
if ev.key_code == .right {
|
||||
app.item_list.get_next_item(1)
|
||||
load_image(mut app)
|
||||
}
|
||||
|
||||
// jump to the next container if possible
|
||||
if ev.key_code == .up {
|
||||
app.item_list.go_to_next_container(1)
|
||||
load_image(mut app)
|
||||
}
|
||||
// jump to the previous container if possible
|
||||
if ev.key_code == .down {
|
||||
app.item_list.go_to_next_container(-1)
|
||||
load_image(mut app)
|
||||
}
|
||||
|
||||
// rotate the image
|
||||
if ev.key_code == .r {
|
||||
app.item_list.rotate(1)
|
||||
}
|
||||
|
||||
// full screen
|
||||
if ev.key_code == .f {
|
||||
println('Full screen state: $sapp.is_fullscreen()')
|
||||
sapp.toggle_fullscreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// drag&drop
|
||||
if ev.typ == .files_droped {
|
||||
app.state = .scanning
|
||||
// set logo texture during scanning
|
||||
show_logo(mut app)
|
||||
|
||||
num := sapp.get_num_dropped_files()
|
||||
mut file_list := []string{}
|
||||
for i in 0 .. num {
|
||||
file_list << sapp.get_dropped_file_path(i)
|
||||
}
|
||||
println('Scanning: $file_list')
|
||||
app.item_list = &Item_list{}
|
||||
app.item_list.loaded = false
|
||||
|
||||
// load_image(mut app)
|
||||
// go app.item_list.get_items_list(file_list)
|
||||
|
||||
load_and_show(file_list, mut app)
|
||||
}
|
||||
}
|
||||
|
||||
fn load_and_show(file_list []string, mut app App) {
|
||||
app.item_list.get_items_list(file_list)
|
||||
load_image(mut app)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Main
|
||||
*
|
||||
******************************************************************************/
|
||||
// is needed for easier diagnostics on windows
|
||||
[console]
|
||||
fn main() {
|
||||
// mut font_path := os.resource_abs_path(os.join_path('../assets/fonts/', 'RobotoMono-Regular.ttf'))
|
||||
font_name := 'RobotoMono-Regular.ttf'
|
||||
font_path := os.join_path(os.temp_dir(), font_name)
|
||||
println('Temporary path for the font file: [$font_path]')
|
||||
|
||||
// if the font doesn't exist create it from the ebedded one
|
||||
if os.exists(font_path) == false {
|
||||
println('Write font [$font_name] in temp folder.')
|
||||
embedded_file := $embed_file('../assets/fonts/RobotoMono-Regular.ttf')
|
||||
os.write_file(font_path, embedded_file.to_string()) or {
|
||||
eprintln('ERROR: not able to write font file to [$font_path]')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// logo image
|
||||
logo_name := 'logo.png'
|
||||
logo_path := os.join_path(os.temp_dir(), logo_name)
|
||||
println('Temporary path for the logo: [$logo_path]')
|
||||
// if the logo doesn't exist create it from the ebedded one
|
||||
if os.exists(logo_path) == false {
|
||||
println('Write logo [$logo_name] in temp folder.')
|
||||
embedded_file := $embed_file('../assets/logo.png')
|
||||
os.write_file(logo_path, embedded_file.to_string()) or {
|
||||
eprintln('ERROR: not able to write logo file to [$logo_path]')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// App init
|
||||
mut app := &App{
|
||||
gg: 0
|
||||
// zip fields
|
||||
zip: 0
|
||||
item_list: 0
|
||||
}
|
||||
|
||||
app.state = .scanning
|
||||
app.logo_path = logo_path
|
||||
app.font_path = font_path
|
||||
|
||||
// Scan all the arguments to find images
|
||||
app.item_list = &Item_list{}
|
||||
// app.item_list.get_items_list(os.args[1..])
|
||||
load_and_show(os.args[1..], mut app)
|
||||
|
||||
app.gg = gg.new_context(
|
||||
width: win_width
|
||||
height: win_height
|
||||
create_window: true
|
||||
window_title: 'V Image viewer 0.8'
|
||||
user_data: app
|
||||
bg_color: bg_color
|
||||
frame_fn: frame
|
||||
init_fn: app_init
|
||||
cleanup_fn: cleanup
|
||||
event_fn: my_event_manager
|
||||
font_path: font_path
|
||||
enable_dragndrop: true
|
||||
max_dropped_files: 64
|
||||
max_dropped_file_path_length: 2048
|
||||
// ui_mode: true
|
||||
)
|
||||
|
||||
app.gg.run()
|
||||
}
|
71
examples/viewer/zip_container.v
Normal file
71
examples/viewer/zip_container.v
Normal file
@ -0,0 +1,71 @@
|
||||
/**********************************************************************
|
||||
*
|
||||
* Zip container manager
|
||||
*
|
||||
* Copyright (c) 2021 Dario Deledda. All rights reserved.
|
||||
* Use of this source code is governed by an MIT license
|
||||
* that can be found in the LICENSE file.
|
||||
*
|
||||
* TODO:
|
||||
**********************************************************************/
|
||||
import szip
|
||||
|
||||
fn (mut il Item_list) scan_zip(path string, in_index int) ? {
|
||||
println('Scanning ZIP [$path]')
|
||||
mut zp := szip.open(path, szip.CompressionLevel.no_compression, szip.OpenMode.read_only) ?
|
||||
n_entries := zp.total() ?
|
||||
// println(n_entries)
|
||||
for index in 0 .. n_entries {
|
||||
zp.open_entry_by_index(index) ?
|
||||
is_dir := zp.is_dir() ?
|
||||
name := zp.name()
|
||||
size := zp.size()
|
||||
// println("$index ${name} ${size:10} $is_dir")
|
||||
|
||||
if !is_dir {
|
||||
ext := get_extension(name)
|
||||
if is_image(ext) == true {
|
||||
il.n_item += 1
|
||||
mut item := Item{
|
||||
need_extract: true
|
||||
path: path
|
||||
name: name.clone()
|
||||
container_index: in_index
|
||||
container_item_index: index
|
||||
i_type: ext
|
||||
n_item: il.n_item
|
||||
drawable: true
|
||||
size: size
|
||||
}
|
||||
il.lst << item
|
||||
}
|
||||
}
|
||||
// IMPORTANT NOTE: don't close the zip entry before we have used all the items!!
|
||||
zp.close_entry()
|
||||
}
|
||||
zp.close()
|
||||
}
|
||||
|
||||
fn (mut app App) load_texture_from_zip() ?(C.sg_image, int, int) {
|
||||
item := app.item_list.lst[app.item_list.item_index]
|
||||
// println("Load from zip [${item.path}]")
|
||||
|
||||
// open the zip
|
||||
if app.zip_index != item.container_index {
|
||||
if app.zip_index >= 0 {
|
||||
app.zip.close()
|
||||
}
|
||||
app.zip_index = item.container_index
|
||||
// println("Opening the zip [${item.path}]")
|
||||
app.zip = szip.open(item.path, szip.CompressionLevel.no_compression, szip.OpenMode.read_only) ?
|
||||
}
|
||||
// println("Now get the image")
|
||||
app.zip.open_entry_by_index(item.container_item_index) ?
|
||||
zip_entry_size := int(item.size)
|
||||
|
||||
app.resize_buf_if_needed(zip_entry_size)
|
||||
|
||||
app.zip.read_entry_buf(app.mem_buf, app.mem_buf_size) ?
|
||||
app.zip.close_entry()
|
||||
return app.load_texture_from_buffer(app.mem_buf, zip_entry_size)
|
||||
}
|
Loading…
Reference in New Issue
Block a user