N64 QOI Demo
A N64 homebrew app that opens QOI images from ROM
Loading...
Searching...
No Matches
qoi_viewer.c
Go to the documentation of this file.
1/*
2
3 qoi_viewer.c
4
5 This source code implements functions related to QOI viewer
6
7 Code licensed under MIT License
8
9 Copyright (c) 2025-2026 Aftersol
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28
29*/
30
33
34#define SIMPLIFIED_QOI_IMPLEMENTATION
35
36#include <stdint.h>
37#include <stdio.h>
38#include <stdlib.h>
39
40#include <string.h>
41
42#include "config.h"
43#include "sQOI.h"
44#include "qoi_viewer.h"
45
46#include <assert.h>
47
51void draw_image(surface_t* disp, qoi_img_info_t info) {
52 const char rgbStr[] = "RGB";
53 const char rgbaStr[] = "RGBA";
54 const char unknownStr[] = "???";
55 const char* channelStr;
56
57 surface_t image = surface_make_linear(
58 buffer0,
59 FMT_RGBA32,
60 info.width,
61 info.height
62 );
63
64 rdpq_attach(disp, NULL);
65
66 rdpq_set_mode_fill(RGBA32(0, 0, 0, 255));
67 rdpq_fill_rectangle(0, 0, 320, 240);
68
69 rdpq_set_mode_standard();
70
71 // draw decoded image into screen
72 rdpq_tex_blit(&image, 0.0, 0.0, NULL);
73
74 if (info.renderDebugFont == true) {
75 if (info.channels == 3) {
76 channelStr = rgbStr;
77 } else if (info.channels == 4) {
78 channelStr = rgbaStr;
79 }
80 else {
81 channelStr = unknownStr;
82 }
83
84 rdpq_text_printf(
85 &(rdpq_textparms_t) {
86 .width = 320-32,
87 .align = ALIGN_LEFT,
88 .wrap = WRAP_WORD,
89 },
90 1,
91 32,
92 32,
93 "N64 QOI Viewer (Revised: %s)\n"
94 "Current Image: %s\n"
95 "Size: %i x %i\n"
96 "Channels: %i (%s)\n"
97 "Decode Time: %f ms",
98 QOI_DEC_REVISION_DATE,
99 info.name,
100 info.width,
101 info.height,
102 info.channels,
103 channelStr,
104 info.decodeTime * 1000.0f
105 );
106 }
107
108 rdpq_detach_show();
109}
110
111
116void openQOIFile(const char* filename, uint8_t* bytes, qoi_img_info_t* info) {
117
118 qoi_desc_t desc;
119 qoi_dec_t dec;
120 qoi_pixel_t px;
121 uint8_t* qoi_bytes;
122 int seek = 0, buffer_size;
123 long long start, end;
124
125 FILE* fp;
126
127 if (!bytes) {
128 info->error = QOI_NULL_BUFFER;
129 return;
130 }
131
132 if (!filename) {
133 info->error = QOI_NO_FILENAME;
134 return;
135 }
136
137 start = timer_ticks();
138 fp = fopen(filename, "rb");
139
140 if (!fp) {
141 info->error = QOI_NO_FILE;
142 return;
143 }
144
145 fseek(fp, 0, SEEK_END);
146 buffer_size = ftell(fp);
147 fseek(fp, 0, SEEK_SET);
148
149 qoi_bytes = (uint8_t*)malloc(buffer_size * sizeof(uint8_t));
150
151 assert(qoi_bytes); // crash if qoi_bytes fails to allocate
152
153 fread(qoi_bytes, 1, buffer_size, fp);
154
155 fclose(fp);
156
157 fp = NULL;
158
159 qoi_desc_init(&desc);
160
161 if (!read_qoi_header(&desc, qoi_bytes)) {
162 info->error = QOI_INVAILD_FILE;
163 goto cleanup;
164 }
165
166 info->width = desc.width;
167 info->height = desc.height;
168 info->channels = desc.channels;
169
170 assertf(
171 info->width * info->height * info->channels <= IMG_BUFFER_SIZE,
172 "%s is too big to open and read\n\
173 To prevent buffer overrun on big sized QOI images,\n\
174 QOI Viewer has been terminated.\n\
175 Make sure your QOI image is a maximum of 320 pixels\n\
176 in width and 240 pixels in height",
177 filename
178 ); // crash to prevent buffer overrun
179
180 dec = (qoi_dec_t){
181 .run = 0,
182 .pad = 0,
183 .pixel_seek = 0,
184 .img_area = desc.width * desc.height,
185 .qoi_len = buffer_size,
186 .data = qoi_bytes,
187 .offset = qoi_bytes + 14
188
189 }; // somehow this compiles
190
191 for (uint8_t element = 0; element < 64; element++)
192 qoi_initalize_pixel(&dec.buffer[element]);
193
194 qoi_set_pixel_rgba(&dec.prev_pixel, 0, 0, 0, 255);
195
196 while (!qoi_dec_done(&dec)) {
197 px = qoi_decode_chunk(&dec);
198
199 // O2 still copy bytes so we use pointer trick to pretend
200 // that pixel data is an integer and copy 4 bytes at a time
201 *(uint32_t*)(bytes+seek) = px.concatenated_pixel_values;
202
203 seek += 4;
204
205 }
206
207 sys_hw_memset(info->name, 0, 256);
208
209 // copy first 255 characters to prevent string overflow
210 memcpy(info->name, filename, strlen(filename) < 256 ? strlen(filename) : 255);
211 info->error = QOI_OK;
212
213cleanup:
214 free(qoi_bytes);
215 qoi_bytes = NULL;
216 end = timer_ticks();
217 info->decodeTime = (float)((float)(end - start) / (float)TICKS_PER_SECOND);
218}
uint8_t buffer0[IMG_BUFFER_SIZE]
Raw image buffer that decoded from qoi image file.
Definition main.c:54
void draw_image(surface_t *disp, qoi_img_info_t info)
This function draws image decoded from QOI.
Definition qoi_viewer.c:51
void openQOIFile(const char *filename, uint8_t *bytes, qoi_img_info_t *info)
This function decodes QOI file from from into the framebuffer.
Definition qoi_viewer.c:116
This header contains declaration of functions related to QOI viewer.
@ QOI_OK
QOI Image decoded successfully.
Definition qoi_viewer.h:57
@ QOI_INVAILD_FILE
Invaild QOI image file found.
Definition qoi_viewer.h:61
@ QOI_NO_FILENAME
Filename to the QOI image not passed to decoder.
Definition qoi_viewer.h:65
@ QOI_NO_FILE
No file found given a filename to the supposed QOI image.
Definition qoi_viewer.h:63
@ QOI_NULL_BUFFER
No buffer for image found.
Definition qoi_viewer.h:59
struct qoi_img_info qoi_img_info_t
Metadata about the QOI image and the QOI image viewer.
#define IMG_BUFFER_SIZE
Image buffer size: 320 pixels in width * 240 pixels in height * 4 channels.
Definition qoi_viewer.h:46
int channels
Number of channels of the QOI image where 3 is RGB and 4 is RGBA.
Definition qoi_viewer.h:77
char name[256]
Names of QOI file.
Definition qoi_viewer.h:86
int height
Height of the QOI image.
Definition qoi_viewer.h:74
float decodeTime
Decoding time in seconds.
Definition qoi_viewer.h:83
int width
Width of the QOI image.
Definition qoi_viewer.h:71
bool renderDebugFont
Whether to toggle displaying debug text upon pressing the Start button on the N64 controller.
Definition qoi_viewer.h:89
qoi_error_code error
Error code as the result of decoding.
Definition qoi_viewer.h:80