1+ #include <stdio.h>
2+ #include <stdlib.h>
3+ #include <string.h>
4+ #include <zlib.h>
5+ #include "image.h"
6+ #include "png.h"
7+
8+ Image * image_create (uint32_t width , uint32_t height ) {
9+ Image * img = malloc (sizeof (Image ));
10+ if (!img ) return NULL ;
11+
12+ img -> width = width ;
13+ img -> height = height ;
14+ img -> data = calloc (width * height * 3 , 1 );
15+ if (!img -> data ) {
16+ free (img );
17+ return NULL ;
18+ }
19+
20+ return img ;
21+ }
22+
23+ Image * image_load (const char * filename ) {
24+ FILE * f = fopen (filename , "rb" );
25+ if (!f ) return NULL ;
26+
27+ // Check PNG signature
28+ unsigned char signature [8 ];
29+ if (fread (signature , 1 , 8 , f ) != 8 ||
30+ signature [0 ] != 0x89 || signature [1 ] != 0x50 || signature [2 ] != 0x4E || signature [3 ] != 0x47 ||
31+ signature [4 ] != 0x0D || signature [5 ] != 0x0A || signature [6 ] != 0x1A || signature [7 ] != 0x0A ) {
32+ fclose (f );
33+ return NULL ;
34+ }
35+
36+ // Read IHDR chunk
37+ uint32_t length , type ;
38+ if (fread (& length , 4 , 1 , f ) != 1 ) {
39+ fclose (f );
40+ return NULL ;
41+ }
42+ length = __builtin_bswap32 (length );
43+ if (length != 13 ) {
44+ fclose (f );
45+ return NULL ;
46+ }
47+
48+ if (fread (& type , 4 , 1 , f ) != 1 ) {
49+ fclose (f );
50+ return NULL ;
51+ }
52+ type = __builtin_bswap32 (type );
53+ if (type != 0x49484452 ) { // "IHDR"
54+ fclose (f );
55+ return NULL ;
56+ }
57+
58+ uint32_t width , height ;
59+ uint8_t bit_depth , color_type , compression , filter , interlace ;
60+ if (fread (& width , 4 , 1 , f ) != 1 || fread (& height , 4 , 1 , f ) != 1 ||
61+ fread (& bit_depth , 1 , 1 , f ) != 1 || fread (& color_type , 1 , 1 , f ) != 1 ||
62+ fread (& compression , 1 , 1 , f ) != 1 || fread (& filter , 1 , 1 , f ) != 1 ||
63+ fread (& interlace , 1 , 1 , f ) != 1 ) {
64+ fclose (f );
65+ return NULL ;
66+ }
67+
68+ width = __builtin_bswap32 (width );
69+ height = __builtin_bswap32 (height );
70+
71+ // Skip IHDR CRC
72+ fseek (f , 4 , SEEK_CUR );
73+
74+ // Read IDAT chunk
75+ if (fread (& length , 4 , 1 , f ) != 1 ) {
76+ fclose (f );
77+ return NULL ;
78+ }
79+ length = __builtin_bswap32 (length );
80+
81+ if (fread (& type , 4 , 1 , f ) != 1 ) {
82+ fclose (f );
83+ return NULL ;
84+ }
85+ type = __builtin_bswap32 (type );
86+ if (type != 0x49444154 ) { // "IDAT"
87+ fclose (f );
88+ return NULL ;
89+ }
90+
91+ // Read compressed data
92+ unsigned char * compressed = malloc (length );
93+ if (!compressed ) {
94+ fclose (f );
95+ return NULL ;
96+ }
97+
98+ if (fread (compressed , 1 , length , f ) != length ) {
99+ free (compressed );
100+ fclose (f );
101+ return NULL ;
102+ }
103+
104+ // Calculate uncompressed size
105+ size_t row_size = width * 3 ;
106+ size_t image_size = height * (row_size + 1 ); // +1 for filter byte per row
107+
108+ // Decompress data using zlib
109+ z_stream strm ;
110+ strm .zalloc = Z_NULL ;
111+ strm .zfree = Z_NULL ;
112+ strm .opaque = Z_NULL ;
113+ strm .avail_in = length ;
114+ strm .next_in = compressed ;
115+
116+ if (inflateInit (& strm ) != Z_OK ) {
117+ free (compressed );
118+ fclose (f );
119+ return NULL ;
120+ }
121+
122+ unsigned char * image_data = malloc (image_size );
123+ if (!image_data ) {
124+ inflateEnd (& strm );
125+ free (compressed );
126+ fclose (f );
127+ return NULL ;
128+ }
129+
130+ strm .avail_out = image_size ;
131+ strm .next_out = image_data ;
132+
133+ if (inflate (& strm , Z_FINISH ) != Z_STREAM_END || strm .total_out != image_size ) {
134+ inflateEnd (& strm );
135+ free (compressed );
136+ free (image_data );
137+ fclose (f );
138+ return NULL ;
139+ }
140+
141+ inflateEnd (& strm );
142+ free (compressed );
143+
144+ // Create image structure
145+ Image * img = malloc (sizeof (Image ));
146+ if (!img ) {
147+ free (image_data );
148+ fclose (f );
149+ return NULL ;
150+ }
151+
152+ img -> width = width ;
153+ img -> height = height ;
154+ img -> data = malloc (width * height * 3 );
155+ if (!img -> data ) {
156+ free (image_data );
157+ free (img );
158+ fclose (f );
159+ return NULL ;
160+ }
161+
162+ // Copy pixel data, skipping filter bytes
163+ for (size_t y = 0 ; y < height ; y ++ ) {
164+ memcpy (img -> data + y * row_size ,
165+ image_data + y * (row_size + 1 ) + 1 ,
166+ row_size );
167+ }
168+
169+ free (image_data );
170+ fclose (f );
171+ return img ;
172+ }
173+
174+ bool image_save (const Image * img , const char * filename ) {
175+ if (!img || !filename ) return false;
176+ return write_png (filename , (const unsigned char * )img -> data , img -> width , img -> height );
177+ }
178+
179+ void image_free (Image * img ) {
180+ if (!img ) return ;
181+ free (img -> data );
182+ free (img );
183+ }
0 commit comments