From 524566f270d9e82e8b60b1493dead91f18c30169 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Dusan=20Gallo=C2=A0?= <> Date: Sun, 7 Nov 2021 10:14:54 +0000 Subject: [PATCH] Image loader for .scr (ZX Spectrum) files --- README.md | 2 +- .../GuideReferenceSupportedFormats.xml | 2 +- src/Makefile.am | 2 + src/filefilter.c | 1 + src/image-load.c | 8 + src/image_load_zxscr.c | 212 ++++++++++++++++++ src/image_load_zxscr.h | 27 +++ 7 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 src/image_load_zxscr.c create mode 100644 src/image_load_zxscr.h diff --git a/README.md b/README.md index b5b23f08..6c09b12a 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Geeqie is a graphics file viewer. Basic features: * output: single image, anaglyph, SBS, mirror, SBS half size (3DTV) * Viewing raster and vector images, in the following formats: -3FR, ANI, APM, ARW, AVIF, BMP, CR2, CR3, CRW, CUR, DDS, DjVu, DNG, ERF, GIF, HEIC, HEIF, ICNS, ICO, JP2. JPE/JPEG/JPG, JPEG XL, JPS, KDC, MEF, MOS, MPO, MRW, NEF, ORF, PBM/PGM/PNM/PPM, PEF, PNG, PSD, PTX, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WEBP, WMF, XBM, XPM. +3FR, ANI, APM, ARW, AVIF, BMP, CR2, CR3, CRW, CUR, DDS, DjVu, DNG, ERF, GIF, HEIC, HEIF, ICNS, ICO, JP2. JPE/JPEG/JPG, JPEG XL, JPS, KDC, MEF, MOS, MPO, MRW, NEF, ORF, PBM/PGM/PNM/PPM, PEF, PNG, PSD, PTX, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SCR (ZX Spectrum), SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WEBP, WMF, XBM, XPM. * Display images in archive files (.ZIP, .RAR etc.). * Animated GIFs are supported. diff --git a/doc/docbook/GuideReferenceSupportedFormats.xml b/doc/docbook/GuideReferenceSupportedFormats.xml index 25d4fcdb..d2367fe2 100644 --- a/doc/docbook/GuideReferenceSupportedFormats.xml +++ b/doc/docbook/GuideReferenceSupportedFormats.xml @@ -1,7 +1,7 @@
Supported File Formats - 3FR, ANI, APM, ARW, BMP, CR2, CRW, CUR, DDS, DNG, ERF, GIF, ICNS, ICO, JPE/JPEG/JPG, JPS, KDC, MEF, MPO, MOS, MRW, NEF, ORF, PEF, PTX, PBM/PGM/PNM/PPM, PNG, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WMF, XBM, XPM, HEIF (primary image only), WebP, DjVu. + 3FR, ANI, APM, ARW, AVIF, BMP, CR2, CR3, CRW, CUR, DDS, DjVu, DNG, ERF, GIF, HEIC, HEIF, ICNS, ICO, JP2. JPE/JPEG/JPG, JPEG XL, JPS, KDC, MEF, MOS, MPO, MRW, NEF, ORF, PBM/PGM/PNM/PPM, PEF, PNG, PSD, PTX, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SCR (ZX Spectrum), SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WEBP, WMF, XBM, XPM. Images in archive files (.zip, .tar etc) can be displayed. Animated GIFs are supported. diff --git a/src/Makefile.am b/src/Makefile.am index 5fbc303e..9a47bb63 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -203,6 +203,8 @@ geeqie_SOURCES = \ image_load_heif.h\ image_load_webp.c\ image_load_webp.h\ + image_load_zxscr.c\ + image_load_zxscr.h\ image_load_djvu.c\ image_load_djvu.h\ image_load_psd.c\ diff --git a/src/filefilter.c b/src/filefilter.c index e79ae30a..478831f0 100644 --- a/src/filefilter.c +++ b/src/filefilter.c @@ -310,6 +310,7 @@ void filter_add_defaults(void) #ifdef HAVE_ARCHIVE filter_add_if_missing("zip", "Archive files", ".zip;.rar;.tar;.tar.gz;.tar.bz2;.tar.xz;.tgz;.tbz;.txz;.cbr;.cbz;.gz;.bz2;.xz;.lzh;.lza;.7z", FORMAT_CLASS_ARCHIVE, FALSE, FALSE, TRUE); #endif + filter_add_if_missing("scr", "ZX Spectrum screen Format", ".scr", FORMAT_CLASS_IMAGE, FALSE, FALSE, TRUE); filter_add_if_missing("psd", "Adobe Photoshop Document", ".psd", FORMAT_CLASS_IMAGE, FALSE, FALSE, TRUE); filter_add_if_missing("apng", "Animated Portable Network Graphic", ".apng", FORMAT_CLASS_IMAGE, FALSE, FALSE, TRUE); } diff --git a/src/image-load.c b/src/image-load.c index e899ada7..ff5bf1c6 100644 --- a/src/image-load.c +++ b/src/image-load.c @@ -34,6 +34,7 @@ #include "image_load_ffmpegthumbnailer.h" #include "image_load_collection.h" #include "image_load_webp.h" +#include "image_load_zxscr.h" #include "image_load_j2k.h" #include "image_load_jpegxl.h" #include "image_load_libraw.h" @@ -835,6 +836,13 @@ static void image_loader_setup_loader(ImageLoader *il) } else #endif + if ((il->bytes_total == 6144 || il->bytes_total == 6912) && + (file_extension_match(il->fd->path, ".scr"))) + { + DEBUG_1("Using custom zxscr loader"); + image_loader_backend_set_zxscr(&il->backend); + } + else if (il->fd->format_class == FORMAT_CLASS_COLLECTION) { DEBUG_1("Using custom collection loader"); diff --git a/src/image_load_zxscr.c b/src/image_load_zxscr.c new file mode 100644 index 00000000..59e6e779 --- /dev/null +++ b/src/image_load_zxscr.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2021 - The Geeqie Team + * + * Author: Dusan Gallo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "main.h" + +#include "image-load.h" +#include "image_load_zxscr.h" + +typedef struct _ImageLoaderZXSCR ImageLoaderZXSCR; +struct _ImageLoaderZXSCR { + ImageLoaderBackendCbAreaUpdated area_updated_cb; + ImageLoaderBackendCbSize size_cb; + ImageLoaderBackendCbAreaPrepared area_prepared_cb; + gpointer data; + GdkPixbuf *pixbuf; + guint requested_width; + guint requested_height; + gboolean abort; +}; + +const guchar palette[2][8][3] = { + { + {0x00, 0x00, 0x00}, + {0x00, 0x00, 0xbf}, + {0xbf, 0x00, 0x00}, + {0xbf, 0x00, 0xbf}, + {0x00, 0xbf, 0x00}, + {0x00, 0xbf, 0xbf}, + {0xbf, 0xbf, 0x00}, + {0xbf, 0xbf, 0xbf} + }, { + {0x00, 0x00, 0x00}, + {0x00, 0x00, 0xff}, + {0xff, 0x00, 0x00}, + {0xff, 0x00, 0xff}, + {0x00, 0xff, 0x00}, + {0x00, 0xff, 0xff}, + {0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff} + } +}; + +static void free_buffer(guchar *pixels, gpointer data) +{ + g_free(pixels); +} + +static gboolean image_loader_zxscr_load(gpointer loader, const guchar *buf, gsize count, GError **error) +{ + ImageLoaderZXSCR *ld = (ImageLoaderZXSCR *) loader; + guint8 *pixels; + gint width, height; + gint row, col, mrow, pxs, i; + guint8 attr, bright, ink, paper; + guint8 *ptr; + + if (count != 6144 && count != 6912) + { + log_printf("Error: zxscr reader error\n"); + return FALSE; + } + + width = 256; + height = 192; + + pixels = g_try_malloc(width * height * 3); + + if (!pixels) + { + log_printf("Error: zxscr reader error\n"); + return FALSE; + } + + ld->pixbuf = gdk_pixbuf_new_from_data(pixels, GDK_COLORSPACE_RGB, FALSE, 8, width, height, width * 3, free_buffer, NULL); + + if (!ld->pixbuf) + { + g_free(pixels); + DEBUG_1("Insufficient memory to open ZXSCR file"); + return FALSE; + } + //let's decode screen + for (row = 0; row < 24; row++) + for (col = 0; col < 32; col++) + { + if (count == 6144) + { + //if we have pixels only, make default white ink on black paper + bright = 0x01; + ink = 0x07; + paper = 0x00; + } + else + { + attr = buf[6144 + row * 32 + col]; + bright = (attr >> 6) & 0x01; + ink = attr & 0x07; + paper = ((attr >> 3) & 0x07); + } + ptr = pixels + (row * 256 + col) * 8 * 3; + + for (mrow = 0; mrow < 8; mrow ++) + { + pxs = buf[(row / 8) * 2048 + mrow * 256 + (row % 8) * 32 + col]; + for (i = 0; i < 8; i++) + { + if (pxs & 0x80) + { + *ptr++ = palette[bright][ink][0]; //r + *ptr++ = palette[bright][ink][1]; //g + *ptr++ = palette[bright][ink][2]; //b + } + else + { + *ptr++ = palette[bright][paper][0]; + *ptr++ = palette[bright][paper][1]; + *ptr++ = palette[bright][paper][2]; + } + pxs <<= 1; + } + ptr += (31 * 8 * 3); + } + } + + ld->area_updated_cb(loader, 0, 0, width, height, ld->data); + + return TRUE; +} + +static gpointer image_loader_zxscr_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data) +{ + ImageLoaderZXSCR *loader = g_new0(ImageLoaderZXSCR, 1); + loader->area_updated_cb = area_updated_cb; + loader->size_cb = size_cb; + loader->area_prepared_cb = area_prepared_cb; + loader->data = data; + return (gpointer) loader; +} + +static void image_loader_zxscr_set_size(gpointer loader, int width, int height) +{ + ImageLoaderZXSCR *ld = (ImageLoaderZXSCR *) loader; + ld->requested_width = width; + ld->requested_height = height; +} + +static GdkPixbuf *image_loader_zxscr_get_pixbuf(gpointer loader) +{ + ImageLoaderZXSCR *ld = (ImageLoaderZXSCR *) loader; + return ld->pixbuf; +} + +static gchar *image_loader_zxscr_get_format_name(gpointer loader) +{ + return g_strdup("zxscr"); +} + +static gchar **image_loader_zxscr_get_format_mime_types(gpointer loader) +{ + static gchar *mime[] = {"application/octet-stream", NULL}; + return g_strdupv(mime); +} + +static gboolean image_loader_zxscr_close(gpointer loader, GError **error) +{ + return TRUE; +} + +static void image_loader_zxscr_abort(gpointer loader) +{ + ImageLoaderZXSCR *ld = (ImageLoaderZXSCR *) loader; + ld->abort = TRUE; +} + +static void image_loader_zxscr_free(gpointer loader) +{ + ImageLoaderZXSCR *ld = (ImageLoaderZXSCR *) loader; + if (ld->pixbuf) g_object_unref(ld->pixbuf); + g_free(ld); +} + +void image_loader_backend_set_zxscr(ImageLoaderBackend *funcs) +{ + funcs->loader_new = image_loader_zxscr_new; + funcs->set_size = image_loader_zxscr_set_size; + funcs->load = image_loader_zxscr_load; + funcs->write = NULL; + funcs->get_pixbuf = image_loader_zxscr_get_pixbuf; + funcs->close = image_loader_zxscr_close; + funcs->abort = image_loader_zxscr_abort; + funcs->free = image_loader_zxscr_free; + funcs->get_format_name = image_loader_zxscr_get_format_name; + funcs->get_format_mime_types = image_loader_zxscr_get_format_mime_types; +} +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/src/image_load_zxscr.h b/src/image_load_zxscr.h new file mode 100644 index 00000000..5de9b3ae --- /dev/null +++ b/src/image_load_zxscr.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 - The Geeqie Team + * + * Author: Dusan Gallo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef IMAGE_LOAD_ZXSCR_H +#define IMAGE_LOAD_ZXSCR_H + +void image_loader_backend_set_zxscr(ImageLoaderBackend *funcs); + +#endif +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ -- 2.20.1