Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
imageio_png.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2010, 2014 johannes hanika.
4 Copyright (C) 2010 Christian Himpel.
5 Copyright (C) 2012, 2014 Ulrich Pegelow.
6 Copyright (C) 2013-2016 Roman Lebedev.
7 Copyright (C) 2013-2014, 2016-2017 Tobias Ellinghaus.
8 Copyright (C) 2019, 2026 Aurélien PIERRE.
9 Copyright (C) 2019 Philippe Weyland.
10 Copyright (C) 2020 Hubert Kowalski.
11 Copyright (C) 2020-2021 Pascal Obry.
12 Copyright (C) 2021 Miloš Komarčević.
13 Copyright (C) 2022 Martin Bařinka.
14 Copyright (C) 2023-2024 Alynx Zhou.
15
16 darktable is free software: you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
20
21 darktable is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with darktable. If not, see <http://www.gnu.org/licenses/>.
28*/
29#include <assert.h>
30#include <inttypes.h>
31#include <memory.h>
32#include <glib/gstdio.h>
33#include <png.h>
34#include <stdio.h>
35#include <strings.h>
36
37#include "system/macros.h"
38#include "system/mem_alloc.h"
40#include "imageio_png.h"
41#include "metadata/exif.h"
42#include "develop/develop.h"
43
44int read_header(const char *filename, dt_imageio_png_t *png)
45{
46 png->f = g_fopen(filename, "rb");
47
48 if(IS_NULL_PTR(png->f)) return 1;
49
50#define NUM_BYTES_CHECK (8)
51
52 png_byte dat[NUM_BYTES_CHECK];
53
54 size_t cnt = fread(dat, 1, NUM_BYTES_CHECK, png->f);
55
56 if(cnt != NUM_BYTES_CHECK || png_sig_cmp(dat, (png_size_t)0, NUM_BYTES_CHECK))
57 {
58 fclose(png->f);
59 return 1;
60 }
61
62 png->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
63
64 if(IS_NULL_PTR(png->png_ptr))
65 {
66 fclose(png->f);
67 return 1;
68 }
69
70 png->info_ptr = png_create_info_struct(png->png_ptr);
71 if(IS_NULL_PTR(png->info_ptr))
72 {
73 fclose(png->f);
74 png_destroy_read_struct(&png->png_ptr, NULL, NULL);
75 return 1;
76 }
77
78 if(setjmp(png_jmpbuf(png->png_ptr)))
79 {
80 fclose(png->f);
81 png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
82 return 1;
83 }
84
85 png_init_io(png->png_ptr, png->f);
86
87 // we checked some bytes
88 png_set_sig_bytes(png->png_ptr, NUM_BYTES_CHECK);
89
90 // image info
91 png_read_info(png->png_ptr, png->info_ptr);
92
93 png->bit_depth = png_get_bit_depth(png->png_ptr, png->info_ptr);
94 png->color_type = png_get_color_type(png->png_ptr, png->info_ptr);
95
96 // image input transformations
97
98 // palette => rgb
99 if(png->color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png->png_ptr);
100
101 // 1, 2, 4 bit => 8 bit
102 if(png->color_type == PNG_COLOR_TYPE_GRAY && png->bit_depth < 8)
103 {
104 png_set_expand_gray_1_2_4_to_8(png->png_ptr);
105 png->bit_depth = 8;
106 }
107
108 // strip alpha channel
109 if(png->color_type & PNG_COLOR_MASK_ALPHA) png_set_strip_alpha(png->png_ptr);
110
111 // grayscale => rgb
112 if(png->color_type == PNG_COLOR_TYPE_GRAY || png->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
113 png_set_gray_to_rgb(png->png_ptr);
114
115 // reflect changes
116 png_read_update_info(png->png_ptr, png->info_ptr);
117
118 // png->bytespp = 3*bit_depth/8;
119 png->width = png_get_image_width(png->png_ptr, png->info_ptr);
120 png->height = png_get_image_height(png->png_ptr, png->info_ptr);
121
122#undef NUM_BYTES_CHECK
123
124 return 0;
125}
126
127
129{
130 if(setjmp(png_jmpbuf(png->png_ptr)))
131 {
132 fclose(png->f);
133 png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
134 return 1;
135 }
136
137 png_bytep *row_pointers = malloc(sizeof(png_bytep) * png->height);
138
139 png_bytep row_pointer = (png_bytep)out;
140 const size_t rowbytes = png_get_rowbytes(png->png_ptr, png->info_ptr);
141
142 for(int y = 0; y < png->height; y++)
143 {
144 row_pointers[y] = row_pointer + (size_t)y * rowbytes;
145 }
146
147 png_read_image(png->png_ptr, row_pointers);
148
149 png_read_end(png->png_ptr, png->info_ptr);
150 png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
151
152 dt_free(row_pointers);
153 fclose(png->f);
154 return 0;
155}
156
157
158
160{
161 const char *ext = filename + strlen(filename);
162 while(*ext != '.' && ext > filename) ext--;
163 if(strncmp(ext, ".png", 4) && strncmp(ext, ".PNG", 4)) return DT_IMAGEIO_FILE_CORRUPTED;
164 if(!img->exif_inited) (void)dt_exif_read(img, filename);
165
166 dt_imageio_png_t image;
167 uint8_t *buf = NULL;
168 uint32_t width, height;
169 uint16_t bpp;
170
171
172 if(read_header(filename, &image) != 0) return DT_IMAGEIO_FILE_CORRUPTED;
173
174 width = img->width = image.width;
175 height = img->height = image.height;
176 bpp = image.bit_depth;
177
178 img->dsc.channels = 4;
179 img->dsc.datatype = TYPE_FLOAT;
180 img->dsc.bpp = 4 * sizeof(float);
181 img->dsc.cst = IOP_CS_RGB; // png is always RGB
182 img->dsc.filters = 0u;
183 img->flags &= ~DT_IMAGE_RAW;
184 img->flags &= ~DT_IMAGE_S_RAW;
185 img->flags &= ~DT_IMAGE_HDR;
186 img->flags |= DT_IMAGE_LDR;
187 img->loader = LOADER_PNG;
188
189 if(IS_NULL_PTR(mbuf))
190 {
191 png_destroy_read_struct(&image.png_ptr, &image.info_ptr, NULL);
192 fclose(image.f);
193 return DT_IMAGEIO_OK;
194 }
195
196 float *mipbuf = (float *)dt_mipmap_cache_alloc(mbuf, img);
197 if(IS_NULL_PTR(mipbuf))
198 {
199 fclose(image.f);
200 png_destroy_read_struct(&image.png_ptr, &image.info_ptr, NULL);
201 fprintf(stderr, "[png_open] could not alloc full buffer for image `%s'\n", img->filename);
203 }
204
206 (size_t)image.height * png_get_rowbytes(image.png_ptr, image.info_ptr),
207 0);
208
209 if(IS_NULL_PTR(buf))
210 {
211 fclose(image.f);
212 png_destroy_read_struct(&image.png_ptr, &image.info_ptr, NULL);
213 fprintf(stderr, "[png_open] could not alloc intermediate buffer for image `%s'\n", img->filename);
215 }
216
217 if(read_image(&image, (void *)buf) != 0)
218 {
220 fprintf(stderr, "[png_open] could not read image `%s'\n", img->filename);
222 }
223
224 for(size_t j = 0; j < height; j++)
225 {
226 if(bpp < 16)
227 for(size_t i = 0; i < width; i++)
228 for(int k = 0; k < 3; k++)
229 mipbuf[4 * (j * width + i) + k] = buf[3 * (j * width + i) + k] * (1.0f / 255.0f);
230 else
231 for(size_t i = 0; i < width; i++)
232 for(int k = 0; k < 3; k++)
233 mipbuf[4 * (j * width + i) + k] = (256.0f * buf[2 * (3 * (j * width + i) + k)]
234 + buf[2 * (3 * (j * width + i) + k) + 1]) * (1.0f / 65535.0f);
235 }
236
238
239 return DT_IMAGEIO_OK;
240}
241
242int dt_imageio_png_read_profile(const char *filename, uint8_t **out)
243{
244 dt_imageio_png_t image;
245 png_charp name;
246 int compression_type;
247 png_uint_32 proflen;
248
249#if PNG_LIBPNG_VER >= 10500 /* 1.5.0 */
250 png_bytep profile;
251#else
252 png_charp profile;
253#endif
254
255 if(!(filename && *filename && out)) return 0;
256
257 if(read_header(filename, &image) != 0) return DT_IMAGEIO_FILE_CORRUPTED;
258
259#ifdef PNG_iCCP_SUPPORTED
260 if(png_get_valid(image.png_ptr, image.info_ptr, PNG_INFO_iCCP) != 0
261 && png_get_iCCP(image.png_ptr, image.info_ptr, &name, &compression_type, &profile, &proflen) != 0)
262 {
263 *out = (uint8_t *)g_malloc(proflen);
264 memcpy(*out, profile, proflen);
265 }
266 else
267#endif
268 proflen = 0;
269
270 png_destroy_read_struct(&image.png_ptr, &image.info_ptr, NULL);
271 fclose(image.f);
272
273 return proflen;
274}
275
276// clang-format off
277// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
278// vim: shiftwidth=2 expandtab tabstop=2 cindent
279// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
280// clang-format on
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
@ IOP_CS_RGB
const dt_colormatrix_t dt_aligned_pixel_t out
int dt_exif_read(dt_image_t *img, const char *path)
Definition exif.cc:1994
What a photograph says about itself: the EXIF, IPTC and XMP tags a camera and a cataloguer write,...
@ TYPE_FLOAT
Definition format.h:56
dt_imageio_retval_t
Definition image.h:91
@ DT_IMAGEIO_OK
Definition image.h:92
@ DT_IMAGEIO_CACHE_FULL
Definition image.h:95
@ DT_IMAGEIO_FILE_CORRUPTED
Definition image.h:94
@ DT_IMAGE_LDR
Definition image.h:122
@ LOADER_PNG
Definition image.h:285
int bpp
int read_image(dt_imageio_png_t *png, void *out)
#define NUM_BYTES_CHECK
int read_header(const char *filename, dt_imageio_png_t *png)
Definition imageio_png.c:44
int dt_imageio_png_read_profile(const char *filename, uint8_t **out)
dt_imageio_retval_t dt_imageio_open_png(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf)
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
void * dt_mipmap_cache_alloc(dt_mipmap_buffer_t *buf, const dt_image_t *img)
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
const char * name
Definition pdf.h:90
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
int32_t height
Definition image.h:397
dt_image_loader_t loader
Definition image.h:417
int32_t exif_inited
Definition image.h:365
int32_t flags
Definition image.h:401
int32_t width
Definition image.h:397
dt_iop_buffer_dsc_t dsc
Definition image.h:419
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:386
png_infop info_ptr
Definition png.c:73
png_structp png_ptr
Definition png.c:72
FILE * f
Definition png.c:71
uint32_t filters
Definition format.h:89
unsigned int channels
Definition format.h:83
dt_iop_buffer_type_t datatype
Definition format.h:85