static void generate_thumbnail_cache() { const int max_mip = DT_MIPMAP_2; fprintf(stderr, _("creating cache directories\n")); char filename[PATH_MAX] = {0}; for(int k=DT_MIPMAP_0;k<=max_mip;k++) { snprintf(filename, sizeof(filename), "%s.d/%d", darktable.mipmap_cache->cachedir, k); fprintf(stderr, _("creating cache directory '%s'\n"), filename); int mkd = g_mkdir_with_parents(filename, 0750); if(mkd) { fprintf(stderr, _("could not create directory '%s'!\n"), filename); return; } } // some progress counter sqlite3_stmt *stmt; uint64_t image_count = 0, counter = 0; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select count(id) from images", -1, &stmt, 0); if(sqlite3_step(stmt) == SQLITE_ROW) image_count = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); // go through all images: DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images", -1, &stmt, 0); // could only alloc max_mip-1, but would need to detect the special case that max==0. const size_t bufsize = (size_t)4 * darktable.mipmap_cache->max_width[max_mip] * darktable.mipmap_cache->max_height[max_mip]; uint8_t *tmp = (uint8_t *)dt_alloc_align(16, bufsize); if(!tmp) { fprintf(stderr, "couldn't allocate temporary memory!\n"); sqlite3_finalize(stmt); return; } const int cache_quality = MIN(100, MAX(10, dt_conf_get_int("database_cache_quality"))); while(sqlite3_step(stmt) == SQLITE_ROW) { const int32_t imgid = sqlite3_column_int(stmt, 0); // check whether all of these files are already there int all_exist = 1; for(int k=max_mip;k>=DT_MIPMAP_0;k--) { snprintf(filename, sizeof(filename), "%s.d/%d/%d.jpg", darktable.mipmap_cache->cachedir, k, imgid); all_exist &= !access(filename, R_OK); } if(all_exist) goto next; dt_mipmap_buffer_t buf; // get largest thumbnail for this image // this one will take care of itself, we'll just write out the lower thumbs manually: dt_mipmap_cache_get(darktable.mipmap_cache, &buf, imgid, max_mip, DT_MIPMAP_BLOCKING, 'r'); if(buf.width > 8 && buf.height > 8) // don't create for skulls for(int k=max_mip-1;k>=DT_MIPMAP_0;k--) { uint32_t width, height; const int wd = darktable.mipmap_cache->max_width[k]; const int ht = darktable.mipmap_cache->max_height[k]; // use exactly the same mechanism as the cache internally to rescale the thumbnail: dt_iop_flip_and_zoom_8(buf.buf, buf.width, buf.height, tmp, wd, ht, 0, &width, &height); snprintf(filename, sizeof(filename), "%s.d/%d/%d.jpg", darktable.mipmap_cache->cachedir, k, imgid); FILE *f = fopen(filename, "wb"); if(f) { // allocate temp memory: uint8_t *blob = (uint8_t *)malloc(bufsize); if(!blob) goto write_error; const int32_t length = dt_imageio_jpeg_compress(tmp, blob, width, height, cache_quality); assert(length <= bufsize); int written = fwrite(blob, sizeof(uint8_t), length, f); if(written != length) { write_error: unlink(filename); } free(blob); fclose(f); } } dt_mipmap_cache_release(darktable.mipmap_cache, &buf); next: counter ++; fprintf(stderr, "\rimage %lu/%lu (%.02f%%) ", counter, image_count, 100.0*counter/(float)image_count); } dt_free_align(tmp); sqlite3_finalize(stmt); fprintf(stderr, "done \n"); }
static void _init_8( uint8_t *buf, uint32_t *width, uint32_t *height, const uint32_t imgid, const dt_mipmap_size_t size) { const uint32_t wd = *width, ht = *height; char filename[DT_MAX_PATH_LEN] = {0}; gboolean from_cache = TRUE; /* do not even try to process file if it isnt available */ dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN, &from_cache); if (strlen(filename) == 0 || !g_file_test(filename, G_FILE_TEST_EXISTS)) { *width = *height = 0; return; } const int altered = dt_image_altered(imgid); int res = 1; const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); const int orientation = dt_image_orientation(cimg); // the orientation for this camera is not read correctly from exiv2, so we need // to go the full libraw path (as the thumbnail will be flipped the wrong way round) const int incompatible = !strncmp(cimg->exif_maker, "Phase One", 9); dt_image_cache_read_release(darktable.image_cache, cimg); // first try exif thumbnail, that's smaller and thus faster to load: if(!altered && !dt_conf_get_bool("never_use_embedded_thumb") && !dt_exif_thumbnail(filename, buf, wd, ht, orientation, width, height)) { res = 0; } else if(!altered && !dt_conf_get_bool("never_use_embedded_thumb") && !incompatible) { // try to load the embedded thumbnail in raw gboolean from_cache = TRUE; memset(filename, 0, DT_MAX_PATH_LEN); dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN, &from_cache); const char *c = filename + strlen(filename); while(*c != '.' && c > filename) c--; if(!strcasecmp(c, ".jpg")) { // try to load jpg dt_imageio_jpeg_t jpg; if(!dt_imageio_jpeg_read_header(filename, &jpg)) { uint8_t *tmp = (uint8_t *)malloc(sizeof(uint8_t)*jpg.width*jpg.height*4); if(!dt_imageio_jpeg_read(&jpg, tmp)) { // scale to fit dt_iop_flip_and_zoom_8(tmp, jpg.width, jpg.height, buf, wd, ht, orientation, width, height); res = 0; } free(tmp); } } else { uint8_t *tmp = 0; int32_t thumb_width, thumb_height, orientation; res = dt_imageio_large_thumbnail(filename, &tmp, &thumb_width, &thumb_height, &orientation); if(!res) { // scale to fit dt_iop_flip_and_zoom_8(tmp, thumb_width, thumb_height, buf, wd, ht, orientation, width, height); free(tmp); } } } if(res) { // try the real thing: rawspeed + pixelpipe dt_imageio_module_format_t format; _dummy_data_t dat; format.bpp = _bpp; format.write_image = _write_image; format.levels = _levels; dat.head.max_width = wd; dat.head.max_height = ht; dat.buf = buf; // export with flags: ignore exif (don't load from disk), don't swap byte order, don't do hq processing, and signal we want thumbnail export res = dt_imageio_export_with_flags(imgid, "unused", &format, (dt_imageio_module_data_t *)&dat, 1, 1, 0, 1, NULL,FALSE,NULL,NULL); if(!res) { // might be smaller, or have a different aspect than what we got as input. *width = dat.head.width; *height = dat.head.height; } } // fprintf(stderr, "[mipmap init 8] export image %u finished (sizes %d %d => %d %d)!\n", imgid, wd, ht, dat.head.width, dat.head.height); // any errors? if(res) { // fprintf(stderr, "[mipmap_cache] could not process thumbnail!\n"); *width = *height = 0; return; } // TODO: various speed optimizations: // TODO: also init all smaller mips! // TODO: use mipf, but: // TODO: if output is cropped, don't use mipf! }
static void _init_8( uint8_t *buf, uint32_t *width, uint32_t *height, const uint32_t imgid, const dt_mipmap_size_t size) { const uint32_t wd = *width, ht = *height; /* do not even try to process file if it isnt available */ char filename[2048] = {0}; dt_image_full_path(imgid, filename, 2048); if (strlen(filename) == 0 || !g_file_test(filename, G_FILE_TEST_EXISTS)) { *width = *height = 0; return; } const int altered = dt_image_altered(imgid); int res = 1; const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); const int orientation = dt_image_orientation(cimg); // the orientation for this camera is not read correctly from exiv2, so we need // to go the full libraw path (as the thumbnail will be flipped the wrong way round) const int incompatible = !strncmp(cimg->exif_maker, "Phase One", 9); dt_image_cache_read_release(darktable.image_cache, cimg); // try to load the embedded thumbnail first if(!altered && !dt_conf_get_bool("never_use_embedded_thumb") && !incompatible) { int ret; char filename[DT_MAX_PATH_LEN]; dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN); const char *c = filename + strlen(filename); while(*c != '.' && c > filename) c--; if(!strcasecmp(c, ".jpg")) { // try to load jpg dt_imageio_jpeg_t jpg; if(!dt_imageio_jpeg_read_header(filename, &jpg)) { uint8_t *tmp = (uint8_t *)malloc(sizeof(uint8_t)*jpg.width*jpg.height*4); if(!dt_imageio_jpeg_read(&jpg, tmp)) { // scale to fit dt_iop_flip_and_zoom_8(tmp, jpg.width, jpg.height, buf, wd, ht, orientation, width, height); res = 0; } free(tmp); } } else { // raw image thumbnail libraw_data_t *raw = libraw_init(0); libraw_processed_image_t *image = NULL; ret = libraw_open_file(raw, filename); if(ret) goto libraw_fail; ret = libraw_unpack_thumb(raw); if(ret) goto libraw_fail; ret = libraw_adjust_sizes_info_only(raw); if(ret) goto libraw_fail; image = libraw_dcraw_make_mem_thumb(raw, &ret); if(!image || ret) goto libraw_fail; const int orientation = raw->sizes.flip; if(image->type == LIBRAW_IMAGE_JPEG) { // JPEG: decode (directly rescaled to mip4) dt_imageio_jpeg_t jpg; if(dt_imageio_jpeg_decompress_header(image->data, image->data_size, &jpg)) goto libraw_fail; uint8_t *tmp = (uint8_t *)malloc(sizeof(uint8_t)*jpg.width*jpg.height*4); if(dt_imageio_jpeg_decompress(&jpg, tmp)) { free(tmp); goto libraw_fail; } // scale to fit dt_iop_flip_and_zoom_8(tmp, jpg.width, jpg.height, buf, wd, ht, orientation, width, height); free(tmp); res = 0; } else { // bmp dt_iop_flip_and_zoom_8(image->data, image->width, image->height, buf, wd, ht, orientation, width, height); res = 0; } // clean up raw stuff. libraw_recycle(raw); libraw_close(raw); free(image); if(0) { libraw_fail: // fprintf(stderr,"[imageio] %s: %s\n", filename, libraw_strerror(ret)); libraw_close(raw); res = 1; } } } if(res) { // try the real thing: rawspeed + pixelpipe dt_imageio_module_format_t format; _dummy_data_t dat; format.bpp = _bpp; format.write_image = _write_image; dat.head.max_width = wd; dat.head.max_height = ht; dat.buf = buf; // export with flags: ignore exif (don't load from disk), don't swap byte order, don't do hq processing, and signal we want thumbnail export res = dt_imageio_export_with_flags(imgid, "unused", &format, (dt_imageio_module_data_t *)&dat, 1, 1, 0, 1); if(!res) { // might be smaller, or have a different aspect than what we got as input. *width = dat.head.width; *height = dat.head.height; } } // fprintf(stderr, "[mipmap init 8] export image %u finished (sizes %d %d => %d %d)!\n", imgid, wd, ht, dat.head.width, dat.head.height); // any errors? if(res) { // fprintf(stderr, "[mipmap_cache] could not process thumbnail!\n"); *width = *height = 0; return; } // TODO: various speed optimizations: // TODO: also init all smaller mips! // TODO: use mipf, but: // TODO: if output is cropped, don't use mipf! }