Skip to content

Commit 7ac2992

Browse files
authored
Add direct mode to dzsave (libvips#3557)
Rather than making a pipeline for each tile, call directly into vips2jpeg. For small (eg. 256x256 tiles), this is a lot quicker.
1 parent 6c82476 commit 7ac2992

File tree

12 files changed

+1409
-912
lines changed

12 files changed

+1409
-912
lines changed

CONTRIBUTING.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,15 @@ $ git commit --amend -a
3838
```
3939

4040
in order to update the last commit with all pending changes.
41+
42+
In an emergency, reformat the entire project with something like:
43+
44+
```shell
45+
find . \
46+
\( -name "*.[hc]" -o -name "*.cc" -o -name "*.cpp" \) \
47+
-not \( -path "./libvips/foreign/libnsgif/*" -o \
48+
-name vips-operators.cpp -o \
49+
-name StandaloneFuzzTargetMain.c -o \
50+
-name profiles.c \) | \
51+
xargs clang-format -i
52+
```

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
- better chunking for small shrinks [jcupitt]
2525
- use alpha range of 0.0 - 1.0 for scRGB images [DarthSim]
2626
- add support for 16-bit float TIFFs [DarthSim]
27+
- add direct mode to dzsave [jcupitt]
2728

2829
18/9/23 8.14.5
2930

libvips/foreign/archive.c

Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
/* wrapper around libarchive
2+
*
3+
* 8/9/23
4+
* - extracted from dzsave
5+
*/
6+
7+
/*
8+
9+
This file is part of VIPS.
10+
11+
VIPS is free software; you can redistribute it and/or modify
12+
it under the terms of the GNU Lesser General Public License as published by
13+
the Free Software Foundation; either version 2 of the License, or
14+
(at your option) any later version.
15+
16+
This program is distributed in the hope that it will be useful,
17+
but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
GNU Lesser General Public License for more details.
20+
21+
You should have received a copy of the GNU Lesser General Public License
22+
along with this program; if not, write to the Free Software
23+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24+
02110-1301 USA
25+
26+
*/
27+
28+
/*
29+
30+
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
31+
32+
*/
33+
34+
/*
35+
#define DEBUG_VERBOSE
36+
#define VIPS_DEBUG
37+
#define DEBUG
38+
*/
39+
40+
#ifdef HAVE_CONFIG_H
41+
#include <config.h>
42+
#endif /*HAVE_CONFIG_H*/
43+
#include <glib/gi18n-lib.h>
44+
45+
#include <stdio.h>
46+
#include <stdlib.h>
47+
#include <string.h>
48+
#include <errno.h>
49+
50+
#include <vips/vips.h>
51+
#include <vips/internal.h>
52+
53+
#include "pforeign.h"
54+
55+
#ifdef HAVE_LIBARCHIVE
56+
#include <archive.h>
57+
#include <archive_entry.h>
58+
59+
static GMutex *vips_libarchive_mutex = NULL;
60+
61+
struct _VipsArchive {
62+
// prepend filenames with this for filesystem output
63+
char *base_dirname;
64+
65+
// write a zip to a target
66+
struct archive *archive;
67+
VipsTarget *target;
68+
};
69+
70+
void
71+
vips__archive_free(VipsArchive *archive)
72+
{
73+
// flush any pending writes to zip output
74+
if (archive->archive)
75+
archive_write_close(archive->archive);
76+
77+
VIPS_FREE(archive->base_dirname);
78+
VIPS_FREEF(archive_write_free, archive->archive);
79+
VIPS_FREE(archive);
80+
}
81+
82+
static ssize_t
83+
zip_write_target_cb(struct archive *a, void *client_data,
84+
const void *data, size_t length)
85+
{
86+
VipsArchive *archive = (VipsArchive *) client_data;
87+
88+
if (vips_target_write(archive->target, data, length))
89+
return -1;
90+
91+
return length;
92+
}
93+
94+
static int
95+
zip_close_target_cb(struct archive *a, void *client_data)
96+
{
97+
VipsArchive *archive = (VipsArchive *) client_data;
98+
99+
if (vips_target_end(archive->target))
100+
return ARCHIVE_FATAL;
101+
102+
return ARCHIVE_OK;
103+
}
104+
105+
static void *
106+
vips__archive_once_init(void *client)
107+
{
108+
vips_libarchive_mutex = vips_g_mutex_new();
109+
110+
return NULL;
111+
}
112+
113+
static void
114+
vips__archive_init(void)
115+
{
116+
static GOnce once = G_ONCE_INIT;
117+
118+
VIPS_ONCE(&once, vips__archive_once_init, NULL);
119+
}
120+
121+
// write to a filesystem directory
122+
VipsArchive *
123+
vips__archive_new_to_dir(const char *base_dirname)
124+
{
125+
VipsArchive *archive;
126+
127+
vips__archive_init();
128+
129+
if (!(archive = VIPS_NEW(NULL, VipsArchive)))
130+
return NULL;
131+
132+
archive->base_dirname = g_strdup(base_dirname);
133+
134+
return archive;
135+
}
136+
137+
// write a zip to a target
138+
VipsArchive *
139+
vips__archive_new_to_target(VipsTarget *target,
140+
const char *base_dirname, int compression)
141+
{
142+
VipsArchive *archive;
143+
144+
#ifdef DEBUG
145+
printf("vips__archive_new_to_target: base_dirname = %s, compression = %d\n",
146+
base_dirname, compression);
147+
#endif /*DEBUG*/
148+
149+
vips__archive_init();
150+
151+
if (!(archive = VIPS_NEW(NULL, VipsArchive)))
152+
return NULL;
153+
154+
archive->target = target;
155+
archive->base_dirname = g_strdup(base_dirname);
156+
157+
if (!(archive->archive = archive_write_new())) {
158+
vips_error("archive", "%s", _("unable to create archive"));
159+
vips__archive_free(archive);
160+
return NULL;
161+
}
162+
163+
/* Set format to zip.
164+
*/
165+
if (archive_write_set_format(archive->archive, ARCHIVE_FORMAT_ZIP)) {
166+
vips_error("archive", "%s", _("unable to set zip format"));
167+
vips__archive_free(archive);
168+
return NULL;
169+
}
170+
171+
/* Remap compression=-1 to compression=6.
172+
*/
173+
if (compression == -1)
174+
compression = 6; /* Z_DEFAULT_COMPRESSION */
175+
176+
#if ARCHIVE_VERSION_NUMBER >= 3002000
177+
/* Deflate compression requires libarchive >= v3.2.0.
178+
* https://github.com/libarchive/libarchive/pull/84
179+
*/
180+
char compression_string[2] = { '0' + compression, 0 };
181+
if (archive_write_set_format_option(archive->archive, "zip",
182+
"compression-level", compression_string)) {
183+
vips_error("archive", "%s", _("unable to set compression"));
184+
vips__archive_free(archive);
185+
return NULL;
186+
}
187+
#else
188+
if (compression > 0)
189+
g_warning("libarchive >= v3.2.0 required for Deflate compression");
190+
#endif
191+
192+
/* Do not pad last block.
193+
*/
194+
if (archive_write_set_bytes_in_last_block(archive->archive, 1)) {
195+
vips_error("archive", "%s", _("unable to set padding"));
196+
vips__archive_free(archive);
197+
return NULL;
198+
}
199+
200+
/* Register target callback functions.
201+
*/
202+
if (archive_write_open(archive->archive, archive, NULL,
203+
zip_write_target_cb, zip_close_target_cb)) {
204+
vips_error("archive", "%s", _("unable to open for write"));
205+
vips__archive_free(archive);
206+
return NULL;
207+
}
208+
209+
return archive;
210+
}
211+
212+
static int
213+
vips__archive_mkdir_zip(VipsArchive *archive, const char *dirname)
214+
{
215+
struct archive_entry *entry;
216+
217+
vips__worker_lock(vips_libarchive_mutex);
218+
219+
if (!(entry = archive_entry_new())) {
220+
vips_error("archive", "%s", _("unable to create entry"));
221+
g_mutex_unlock(vips_libarchive_mutex);
222+
return -1;
223+
}
224+
225+
char *path;
226+
227+
path = g_build_filename(archive->base_dirname, dirname, NULL);
228+
229+
archive_entry_set_pathname(entry, path);
230+
archive_entry_set_mode(entry, S_IFDIR | 0755);
231+
232+
if (archive_write_header(archive->archive, entry)) {
233+
char *utf8name = g_filename_display_name(path);
234+
vips_error("archive", _("unable to add directory \"%s\", %s"),
235+
utf8name, archive_error_string(archive->archive));
236+
g_free(utf8name);
237+
g_free(path);
238+
archive_entry_free(entry);
239+
g_mutex_unlock(vips_libarchive_mutex);
240+
return -1;
241+
}
242+
243+
archive_entry_free(entry);
244+
g_free(path);
245+
g_mutex_unlock(vips_libarchive_mutex);
246+
247+
return 0;
248+
}
249+
250+
static int
251+
vips__archive_mkdir_file(VipsArchive *archive, const char *dirname)
252+
{
253+
char *path;
254+
255+
path = g_build_filename(archive->base_dirname, dirname, NULL);
256+
257+
if (g_mkdir_with_parents(path, 0777) &&
258+
errno != EEXIST) {
259+
int save_errno = errno;
260+
char *utf8name;
261+
262+
utf8name = g_filename_display_name(path);
263+
vips_error("archive", _("unable to create directory \"%s\", %s"),
264+
utf8name, g_strerror(save_errno));
265+
266+
g_free(utf8name);
267+
g_free(path);
268+
269+
return -1;
270+
}
271+
272+
g_free(path);
273+
274+
return 0;
275+
}
276+
277+
int
278+
vips__archive_mkdir(VipsArchive *archive, const char *dirname)
279+
{
280+
return ((archive->archive)
281+
? vips__archive_mkdir_zip
282+
: vips__archive_mkdir_file)(archive, dirname);
283+
}
284+
285+
static int
286+
vips__archive_mkfile_zip(VipsArchive *archive,
287+
const char *filename, void *buf, size_t len)
288+
{
289+
struct archive_entry *entry;
290+
291+
vips__worker_lock(vips_libarchive_mutex);
292+
293+
if (!(entry = archive_entry_new())) {
294+
vips_error("archive", "%s", _("unable to create entry"));
295+
g_mutex_unlock(vips_libarchive_mutex);
296+
return -1;
297+
}
298+
299+
char *path;
300+
301+
path = g_build_filename(archive->base_dirname, filename, NULL);
302+
303+
archive_entry_set_pathname(entry, path);
304+
archive_entry_set_mode(entry, S_IFREG | 0664);
305+
archive_entry_set_size(entry, len);
306+
307+
g_free(path);
308+
309+
if (archive_write_header(archive->archive, entry)) {
310+
vips_error("archive", "%s", _("unable to write header"));
311+
archive_entry_free(entry);
312+
g_mutex_unlock(vips_libarchive_mutex);
313+
return -1;
314+
}
315+
316+
archive_entry_free(entry);
317+
318+
if (archive_write_data(archive->archive, buf, len) != len) {
319+
vips_error("archive", "%s", _("unable to write data"));
320+
g_mutex_unlock(vips_libarchive_mutex);
321+
return -1;
322+
}
323+
324+
g_mutex_unlock(vips_libarchive_mutex);
325+
326+
return 0;
327+
}
328+
329+
static int
330+
vips__archive_mkfile_file(VipsArchive *archive,
331+
const char *filename, void *buf, size_t len)
332+
{
333+
char *path;
334+
FILE *f;
335+
336+
path = g_build_filename(archive->base_dirname, filename, NULL);
337+
338+
if (!(f = vips__file_open_write(path, TRUE))) {
339+
g_free(path);
340+
return -1;
341+
}
342+
343+
if (vips__file_write(buf, sizeof(char), len, f)) {
344+
g_free(path);
345+
fclose(f);
346+
return -1;
347+
}
348+
349+
fclose(f);
350+
g_free(path);
351+
352+
return 0;
353+
}
354+
355+
int
356+
vips__archive_mkfile(VipsArchive *archive,
357+
const char *filename, void *buf, size_t len)
358+
{
359+
return ((archive->archive)
360+
? vips__archive_mkfile_zip
361+
: vips__archive_mkfile_file)(archive, filename, buf, len);
362+
}
363+
364+
#endif /*HAVE_LIBARCHIVE*/

0 commit comments

Comments
 (0)