83
83
* - add @skip_blanks
84
84
* 21/10/19
85
85
* - add @no_strip
86
+ * 9/11/19
87
+ * - add IIIF layout
86
88
*/
87
89
88
90
/*
@@ -937,6 +939,86 @@ write_blank( VipsForeignSaveDz *dz )
937
939
return ( 0 );
938
940
}
939
941
942
+ /* Write IIIF JSON metadata.
943
+ */
944
+ static int
945
+ write_json ( VipsForeignSaveDz * dz )
946
+ {
947
+ GsfOutput * out ;
948
+ char buf [VIPS_PATH_MAX ];
949
+ int i ;
950
+
951
+ out = vips_gsf_path ( dz -> tree , "info.json" , NULL );
952
+
953
+ gsf_output_printf ( out ,
954
+ "{\n"
955
+ " \"@context\": \"http://iiif.io/api/image/2/context.json\",\n"
956
+ " \"@id\": \"https://example.com/iiif/apple\",\n"
957
+ " \"profile\": [\n"
958
+ " \"http://iiif.io/api/image/2/level0.json\",\n"
959
+ " {\n"
960
+ " \"formats\": [\n"
961
+ " \"jpg\"\n"
962
+ " ],\n"
963
+ " \"qualities\": [\n"
964
+ " \"default\"\n"
965
+ " ]\n"
966
+ " }\n"
967
+ " ],\n"
968
+ " \"protocol\": \"http://iiif.io/api/image\",\n"
969
+ " \"sizes\": [\n" );
970
+
971
+ for ( i = 0 ; i < dz -> layer -> n + 5 ; i ++ ) {
972
+ gsf_output_printf ( out ,
973
+ " {\n"
974
+ " \"width\": %d,\n"
975
+ " \"height\": \"full\"\n"
976
+ " }" ,
977
+ 1 << (i + 4 ) );
978
+ if ( i != dz -> layer -> n - 4 )
979
+ gsf_output_printf ( out , "," );
980
+ gsf_output_printf ( out , "\n" );
981
+ }
982
+
983
+ gsf_output_printf ( out ,
984
+ " ],\n" );
985
+
986
+ gsf_output_printf ( out ,
987
+ " \"tiles\": [\n"
988
+ " {\n"
989
+ " \"scalefactors\": [\n" );
990
+
991
+ for ( i = 0 ; i < dz -> layer -> n ; i ++ ) {
992
+ gsf_output_printf ( out ,
993
+ " %d" ,
994
+ 1 << i );
995
+ if ( i != dz -> layer -> n - 1 )
996
+ gsf_output_printf ( out , "," );
997
+ gsf_output_printf ( out , "\n" );
998
+ }
999
+
1000
+ gsf_output_printf ( out ,
1001
+ " ],\n"
1002
+ " \"width\": %d\n"
1003
+ " }\n"
1004
+ " ],\n" , dz -> tile_size );
1005
+
1006
+ gsf_output_printf ( out ,
1007
+ " \"width\": %d,\n"
1008
+ " \"height\": %d\n" ,
1009
+ dz -> layer -> image -> Xsize ,
1010
+ dz -> layer -> image -> Ysize );
1011
+
1012
+ gsf_output_printf ( out ,
1013
+ "}\n" );
1014
+
1015
+ (void ) gsf_output_close ( out );
1016
+ g_object_unref ( out );
1017
+
1018
+ return ( 0 );
1019
+ }
1020
+
1021
+
940
1022
static int
941
1023
write_vips_meta ( VipsForeignSaveDz * dz )
942
1024
{
@@ -1306,6 +1388,40 @@ tile_name( Layer *layer, int x, int y )
1306
1388
1307
1389
break ;
1308
1390
1391
+ case VIPS_FOREIGN_DZ_LAYOUT_IIIF :
1392
+ {
1393
+ /* Tiles are addressed in full resolution coordinates, so
1394
+ * scale up by layer->sub and dz->tile_size
1395
+ */
1396
+ int left = x * dz -> tile_size * layer -> sub ;
1397
+ int top = y * dz -> tile_size * layer -> sub ;
1398
+ int width = VIPS_MIN ( dz -> tile_size * layer -> sub ,
1399
+ layer -> width * layer -> sub - left );
1400
+ int height = VIPS_MIN ( dz -> tile_size * layer -> sub ,
1401
+ layer -> height * layer -> sub - top );
1402
+
1403
+ /* IIIF "size" is just real tile width, I think.
1404
+ *
1405
+ * TODO .. .is this right? shouldn't it be the smaller of
1406
+ * width and height?
1407
+ */
1408
+ int size = VIPS_MIN ( dz -> tile_size ,
1409
+ layer -> width - x * dz -> tile_size );
1410
+
1411
+ vips_snprintf ( dirname , VIPS_PATH_MAX , "%d,%d,%d,%d" ,
1412
+ left , top , width , height );
1413
+ vips_snprintf ( dirname2 , VIPS_PATH_MAX , "%d," , size );
1414
+ vips_snprintf ( name , VIPS_PATH_MAX , "default%s" ,
1415
+ dz -> file_suffix );
1416
+
1417
+ /* "0" is rotation and is always 0.
1418
+ */
1419
+ out = vips_gsf_path ( dz -> tree ,
1420
+ name , dirname , dirname2 , "0" , NULL );
1421
+ }
1422
+
1423
+ break ;
1424
+
1309
1425
default :
1310
1426
g_assert_not_reached ();
1311
1427
@@ -1814,10 +1930,11 @@ vips_foreign_save_dz_build( VipsObject *object )
1814
1930
VipsObjectClass * class = VIPS_OBJECT_GET_CLASS ( dz );
1815
1931
VipsRect real_pixels ;
1816
1932
1817
- /* Google and zoomify default to zero overlap, ".jpg".
1933
+ /* Google, zoomify and iiif default to zero overlap, ".jpg".
1818
1934
*/
1819
1935
if ( dz -> layout == VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY ||
1820
- dz -> layout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE ) {
1936
+ dz -> layout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE ||
1937
+ dz -> layout == VIPS_FOREIGN_DZ_LAYOUT_IIIF ) {
1821
1938
if ( !vips_object_argument_isset ( object , "overlap" ) )
1822
1939
dz -> overlap = 0 ;
1823
1940
if ( !vips_object_argument_isset ( object , "suffix" ) )
@@ -1832,6 +1949,13 @@ vips_foreign_save_dz_build( VipsObject *object )
1832
1949
dz -> tile_size = 256 ;
1833
1950
}
1834
1951
1952
+ /* Some iif writers default to 256, some to 512. We pick 512.
1953
+ */
1954
+ if ( dz -> layout == VIPS_FOREIGN_DZ_LAYOUT_IIIF ) {
1955
+ if ( !vips_object_argument_isset ( object , "tile_size" ) )
1956
+ dz -> tile_size = 512 ;
1957
+ }
1958
+
1835
1959
/* skip_blanks defaults to 5 in google mode.
1836
1960
*/
1837
1961
if ( dz -> layout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE &&
@@ -2136,6 +2260,11 @@ vips_foreign_save_dz_build( VipsObject *object )
2136
2260
return ( -1 );
2137
2261
break ;
2138
2262
2263
+ case VIPS_FOREIGN_DZ_LAYOUT_IIIF :
2264
+ if ( write_json ( dz ) )
2265
+ return ( -1 );
2266
+ break ;
2267
+
2139
2268
default :
2140
2269
g_assert_not_reached ();
2141
2270
}
0 commit comments