diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2011-02-17 14:27:30 +0100 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2011-02-17 14:27:30 +0100 |
commit | 0c08f2609d818276427d1b99777aadeb406175b0 (patch) | |
tree | c83fa3bccc8575759c8327255a37b921a03aace0 | |
download | libpcd-0c08f2609d818276427d1b99777aadeb406175b0.tar.gz |
import 1.0.1 release tarball1.0.1
-rw-r--r-- | GNUmakefile | 64 | ||||
-rw-r--r-- | README | 19 | ||||
-rw-r--r-- | debian/changelog | 16 | ||||
-rw-r--r-- | debian/control | 20 | ||||
-rw-r--r-- | debian/copyright | 21 | ||||
-rw-r--r-- | debian/libpcd-dev.files | 3 | ||||
-rw-r--r-- | debian/libpcd2.files | 1 | ||||
-rwxr-xr-x | debian/rules | 48 | ||||
-rw-r--r-- | file.c | 249 | ||||
-rw-r--r-- | huff.c | 225 | ||||
-rw-r--r-- | inter.c | 96 | ||||
-rw-r--r-- | pcd.css | 5 | ||||
-rw-r--r-- | pcd.h | 88 | ||||
-rw-r--r-- | pcd.html | 51 | ||||
-rw-r--r-- | yuv2rgb.c | 469 |
15 files changed, 1375 insertions, 0 deletions
diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..e007f0a --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,64 @@ + +DESTDIR := +prefix := /usr/local + +incdir := $(DESTDIR)$(prefix)/include +libdir := $(DESTDIR)$(prefix)/lib + +CC := gcc +WARN := -Wall -Wmissing-prototypes -Wstrict-prototypes -Wpointer-arith +CFLAGS ?= -g -O2 +CFLAGS += $(WARN) + +MAJOR := 2 +MINOR := 2.0.1 +SONAME := libpcd.so.$(MAJOR) +TARGETS := libpcd.a libpcd.so.$(MINOR) + +SRCS := file.c yuv2rgb.c huff.c inter.c +STATIC := $(subst .c,.o,$(SRCS)) +SHARED := $(subst .c,.pic.o,$(SRCS)) +depfile = $(subst .o,.dep,$@) + +all build: $(TARGETS) + +%.o: %.c + $(CC) $(CFLAGS) -Wp,-MD,$(depfile) -c -o $@ $< + +%.pic.o: %.c + $(CC) $(CFLAGS) -Wp,-MD,$(depfile) -fPIC -c -o $@ $< + +libpcd.a: $(STATIC) + rm -f $@ + ar rc $@ $(STATIC) + ranlib $@ + +libpcd.so.$(MINOR): $(SHARED) + rm -f $@ + $(CC) $(CFLAGS) -shared -Wl,-soname,$(SONAME) -o $@ $(SHARED) + +install: + install -d $(libdir) + install -d $(incdir) + install -m644 libpcd.a $(libdir) + install -m755 libpcd.so.$(MINOR) $(libdir) + ln -s libpcd.so.$(MINOR) $(libdir)/libpcd.so.$(MAJOR) + ln -s libpcd.so.$(MINOR) $(libdir)/libpcd.so + install -m644 pcd.h $(incdir) + +clean: + -rm -f *~ *.bak *.dep $(STATIC) $(SHARED) + +realclean distclean: clean + -rm -f libpcd.a libpcd.so.$(MAJOR) libpcd.so.$(MINOR) + +# auto dependencies +-include *.dep + +# debian stuff +dsc source: distclean + dpkg-buildpackage -S -us -uc -rfakeroot + +debs pbuild: dsc + sudo /usr/sbin/pbuilder build ../libpcd_*.dsc + @@ -0,0 +1,19 @@ + +libpcd - reading PhotoCD images +------------------------------- + +This is a tiny library for decoding PhotoCD images. Its not exactly +new, it exists for years. I've just released it separately for the +first time now. It used to come bundled with xpcd. + +Some documentation is in pcd.html. + +Use "make" to build static and shared elf libraries. +Use "make install" as root to install them. + +Have fun, + + Gerd + +-- +Gerd Knorr <kraxel@bytesex.org> diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..f109587 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,16 @@ +libpcd (1.0.1) unstable; urgency=low + + * fix a number of 64bit bugs (s/unsigned long/uint32_t/ + in several places ...). + + -- Gerd Knorr <kraxel@debian.org> Tue, 22 Jul 2003 11:58:53 +0200 + +libpcd (1.0) unstable; urgency=low + + * initial release (closes: #132780). + + -- Gerd Knorr <kraxel@debian.org> Fri, 8 Feb 2002 14:23:46 +0100 + +Local variables: +mode: debian-changelog +End: diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..264e263 --- /dev/null +++ b/debian/control @@ -0,0 +1,20 @@ +Source: libpcd +Section: devel +Priority: optional +Build-depends: debhelper (>=3.0) +Maintainer: Gerd Knorr <kraxel@debian.org> +Standards-Version: 3.6.0 + +Package: libpcd2 +Architecture: any +Section: devel +Depends: ${shlibs:Depends} +Description: A library for reading PhotoCD images + A library for reading PhotoCD images + +Package: libpcd-dev +Architecture: any +Section: devel +Depends: libpcd2 (= ${Source-Version}) +Description: Header files and static library for libpcd + Header files and static library for libpcd diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..68fc1e8 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,21 @@ +This is libpcd, written and maintained by Gerd Knorr <kraxel@bytesex.org> + +The original source can always be found at: + ftp://ftp.debian.org/dists/unstable/main/source/ + +Copyright (C) 1996-2002 Gerd Knorr + + 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 with + the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA diff --git a/debian/libpcd-dev.files b/debian/libpcd-dev.files new file mode 100644 index 0000000..40d9aba --- /dev/null +++ b/debian/libpcd-dev.files @@ -0,0 +1,3 @@ +/usr/lib/libpcd.a +/usr/lib/libpcd.so +/usr/include/pcd.h diff --git a/debian/libpcd2.files b/debian/libpcd2.files new file mode 100644 index 0000000..352d677 --- /dev/null +++ b/debian/libpcd2.files @@ -0,0 +1 @@ +/usr/lib/libpcd.so.* diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..77ced88 --- /dev/null +++ b/debian/rules @@ -0,0 +1,48 @@ +#!/usr/bin/make -f + +HERE := $(shell pwd) +DEST := $(HERE)/debian/tmp + +# debhelper +export DH_COMPAT=3 + +build: + $(MAKE) prefix=/usr $@ + +clean: + dh_testdir + dh_testroot + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + rm -rf $(DEST) + make DESTDIR=$(DEST) prefix=/usr install + + dh_movefiles + +binary-indep: build install +binary-arch: build install + dh_testdir + dh_testroot + dh_installdocs -p libpcd2 README + dh_installdocs -p libpcd-dev README pcd.html pcd.css + dh_installchangelogs + dh_link + dh_strip + dh_compress + dh_fixperms + dh_makeshlibs -V + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install + @@ -0,0 +1,249 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/mman.h> + +#include "pcd.h" + +char pcd_rotor[] = +{'-', '\\', '|', '/'}; +int pcd_img_start[] = +{0 /*dummy */ , 8192, 47104, 196608}; +int pcd_def_width[] = +{0 /*dummy */ , 192, 384, 768, 1536, 3072, 6144}; +int pcd_def_height[] = +{0 /*dummy */ , 128, 256, 512, 1024, 2048, 4096}; +char pcd_errmsg[512]; + +int +pcd_open(struct PCD_IMAGE *img, char *filename) +{ + int fd; + + pcd_get_LUT_init(); + memset(img, 0, sizeof(struct PCD_IMAGE)); + + fd = open(filename, O_RDONLY); + if (-1 == fd) { + sprintf(pcd_errmsg, "open %s: %s", filename, strerror(errno)); + return -1; + } + img->size = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + img->mmap = mmap(NULL, img->size, PROT_READ, MAP_SHARED, fd, 0); + if ((void *) -1 == img->mmap) { + sprintf(pcd_errmsg, "mmap %s: %s", filename, strerror(errno)); + pcd_close(img); + return -1; + } + close(fd); + if (0 == strncmp("PCD_OPA", img->mmap, 7)) { + /* this is the thumbnails file */ + img->thumbnails = (int) img->mmap[10] << 8 | (int) img->mmap[11]; + } else { + if (img->size < 786432) { + sprintf(pcd_errmsg, "%s: probably not a PhotoCD image (too small)", + filename); + pcd_close(img); + return -1; + } + } + return img->thumbnails; +} + +int +pcd_get_rot(struct PCD_IMAGE *img, int nr) +{ + if (img->thumbnails) { + return img->mmap[12 + nr] & 3; + } else { + return img->mmap[0x48] & 3; + } +} + +int +pcd_get_maxres(struct PCD_IMAGE *img) +{ + if (img->thumbnails) { + return 1; + } else { + if (img->size == 786432) + return 3; + else + return 5; + } +} + +int +pcd_select(struct PCD_IMAGE *img, int res, int nr, int gray, int verbose, + int rot, int *left, int *top, int *width, int *height) +{ + int y; + unsigned char *ptr; + + /* free old stuff */ + pcd_free(img); + + /* sanity checks... */ + if (0 == img->thumbnails) { + if (res < 1 || res > 5) { + sprintf(pcd_errmsg, "invalid resolution (%i) specified", res); + return -1; + } + if (img->size == 786432 && res > 3) { + sprintf(pcd_errmsg, + "PhotoCD file contains only the three lower resolutions"); + return -1; + } + } else { + if (nr < 0 || nr >= img->thumbnails) { + sprintf(pcd_errmsg, + "thumbnail number (%i) out of range", nr); + return -1; + } + } + + /* width/height == 0: fill in default image size */ + if (*left == 0 && *width == 0) + *width = PCD_WIDTH(res, rot); + if (*top == 0 && *height == 0) + *height = PCD_HEIGHT(res, rot); + + if (5 == res) + *left &= ~7, *top &= ~7, *width &= ~7, *height &= ~7; + else if (4 == res) + *left &= ~3, *top &= ~3, *width &= ~3, *height &= ~3; + else + *left &= ~1, *top &= ~1, *width &= ~1, *height &= ~1; + if (*left < 0 || *top < 0 || + *width < 1 || *height < 1 || + *left + *width > PCD_WIDTH(res, rot) || + *top + *height > PCD_HEIGHT(res, rot)) { + sprintf(pcd_errmsg, "specified area (%ix%i+%i+%i) invalid", + *width, *height, *left, *top); + return -1; + } + /* recalc coordinates (rotation) */ + switch (rot) { + case 0: /* none */ + img->left = *left; + img->top = *top; + img->width = *width; + img->height = *height; + break; + case 1: /* 90° ccw */ + img->left = PCD_HEIGHT(res, rot) - *top - *height; + img->top = *left; + img->width = *height; + img->height = *width; + break; + case 2: /* 180° */ + img->left = PCD_WIDTH(res, rot) - *left - *width; + img->top = PCD_HEIGHT(res, rot) - *top - *height; + img->width = *width; + img->height = *height; + break; + case 3: /* 90° cw */ + img->left = *top; + img->top = PCD_WIDTH(res, rot) - *left - *width; + img->width = *height; + img->height = *width; + break; + default: + sprintf(pcd_errmsg, "specified orientation (%i) invalid", rot); + return -1; + } + /* prepeare */ + img->res = res; + img->nr = nr; + img->gray = gray; + img->verbose = verbose; + img->rot = rot; + img->luma = malloc(img->height * sizeof(unsigned char *)); + img->red = malloc(img->height * sizeof(unsigned char *) >> 1); + img->blue = malloc(img->height * sizeof(unsigned char *) >> 1); + + if (img->luma == NULL || + img->red == NULL || + img->blue == NULL) { + sprintf(pcd_errmsg, "out of memory (malloc failed)"); + pcd_free(img); + return -1; + } + if (res <= 3) { + /* just fill in pointers */ + if (img->thumbnails) { + ptr = img->mmap + 10240 + 36864 * nr + + (pcd_def_width[res] >> 1) * 3 * img->top; + } else { + ptr = img->mmap + pcd_img_start[res] + + (pcd_def_width[res] >> 1) * 3 * img->top; + } + for (y = 0; y < img->height; y += 2, ptr += (pcd_def_width[res] >> 1) * 6) { + img->luma[y] = ptr + img->left; + img->luma[y + 1] = ptr + img->left + (pcd_def_width[res] >> 1) * 2; + img->blue[y >> 1] = ptr + (img->left >> 1) + (pcd_def_width[res] >> 1) * 4; + img->red[y >> 1] = ptr + (img->left >> 1) + (pcd_def_width[res] >> 1) * 5; + } + } else { + /* high res, have to malloc memory */ + img->data = malloc(img->width * img->height * 3 / 2); + if (img->data == NULL) { + sprintf(pcd_errmsg, "out of memory (malloc failed)"); + pcd_free(img); + return -1; + } + ptr = img->data; + for (y = 0; y < img->height; y++, ptr += img->width) + img->luma[y] = ptr; + for (y = 0; y < img->height >> 1; y++, ptr += img->width >> 1) + img->blue[y] = ptr; + for (y = 0; y < img->height >> 1; y++, ptr += img->width >> 1) + img->red[y] = ptr; + } + return 0; +} + +int +pcd_free(struct PCD_IMAGE *img) +{ + img->res = 0; + if (img->data) + free(img->data); + if (img->luma) + free(img->luma); + if (img->red) + free(img->red); + if (img->blue) + free(img->blue); + if (img->seq1) + free(img->seq1); + if (img->len1) + free(img->len1); + if (img->seq2) + free(img->seq2); + if (img->len2) + free(img->len2); + if (img->seq3) + free(img->seq3); + if (img->len3) + free(img->len3); + img->data = NULL; + img->luma = img->red = img->blue = NULL; + img->seq1 = img->seq2 = img->seq3 = NULL; + img->len1 = img->len2 = img->len3 = NULL; + return 0; +} + +int +pcd_close(struct PCD_IMAGE *img) +{ + pcd_free(img); + munmap(img->mmap, img->size); + memset(img, 0, sizeof(struct PCD_IMAGE)); + + return 0; +} @@ -0,0 +1,225 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pcd.h" + +#define HUFF1 0xc2000 + +int +pcd_read_htable(unsigned char *src, + unsigned char **pseq, unsigned char **pbits) +{ + int len, seq, seq2, bits, i, j; + + if (*pseq) + free(*pseq); + if (*pbits) + free(*pbits); + *pseq = malloc(0x10000 * sizeof(char)); + memset(*pseq, 0, 0x10000 * sizeof(char)); + *pbits = malloc(0x10000 * sizeof(char)); + memset(*pbits, 0, 0x10000 * sizeof(char)); + + if (*pseq == NULL || *pbits == NULL) + return -1; + for (i = 1, len = *src; len >= 0; i += 4, len--) { + seq = ((int) src[i + 1] << 8 | (int) src[i + 2]); + bits = src[i] + 1; + seq2 = seq + (0x10000 >> bits); + for (j = seq; j < seq2; j++) { + if ((*pbits)[j]) { + sprintf(pcd_errmsg, "Invalid huffmann code table, seems the file is'nt a PhotoCD image"); + return -1; + } + (*pseq)[j] = src[i + 3]; + (*pbits)[j] = bits; + } + } + return i; +} + +#define SETSHIFT { shiftreg=(((stream[0]<<16) | (stream[1]<<8 ) | \ + (stream[2])) >> (8-bit)) & 0xffff; } + +#define LEFTSHIFT { shiftreg=((shiftreg<<1) & 0xffff) | \ + ((stream[2]>>(7-bit++))&1); stream += bit>>3; bit &= 7; } + +static int +pcd_un_huff(struct PCD_IMAGE *img, unsigned char *start, int run) +{ + register int shiftreg, bit; + unsigned char *stream = start; + int y, type, shift; + int height, y1, y2; + + switch (run) { + case 1: + height = pcd_def_height[4]; + y1 = img->top >> (img->res - 4); + y2 = (img->top + img->height) >> (img->res - 4); + break; + case 2: + height = pcd_def_height[5]; + y1 = img->top >> (img->res - 5); + y2 = (img->top + img->height) >> (img->res - 5); + break; + default: + fprintf(stderr, "internal error: pcd_decode: run %i ???\n", run); + exit(1); + } + + for (y = 0; y < height;) { + ROTOR(y); + for (;;) { + bit = 0; + stream = memchr(stream, 0xff, 10240); + if (stream[1] == 0xff) + break; + stream++; + } + SETSHIFT; + while (shiftreg != 0xfffe) + LEFTSHIFT; + stream += 2; + SETSHIFT; + y = (shiftreg >> 1) & 0x1fff; + type = (shiftreg >> 14); + stream += 2; + SETSHIFT; + + if (y > height) { + sprintf(pcd_errmsg, "Oops: invalid line nr (y=%i)\n", y); + return -1; + } + if (y < y1 || y >= y2) + continue; + if (img->gray && type) + return 0; /* cut color decoding */ + + { + register unsigned char *data; + register int x; + unsigned char *seq; + unsigned char *bits; + int x1, x2; + + switch (type) { + case 0: + shift = 0; + seq = img->seq1; + bits = img->len1; + data = img->luma[(y - y1) >> shift]; + break; + case 2: + shift = 1; + seq = img->seq2; + bits = img->len2; + data = img->blue[(y - y1) >> shift]; + break; + case 3: + shift = 1; + seq = img->seq3; + bits = img->len3; + data = img->red[(y - y1) >> shift]; + break; + default: + sprintf(pcd_errmsg, "Oops: invalid line type (type=%i)\n", type); + return -1; + } + + if (run == 1) { + x1 = img->left >> (img->res - 4 + shift); + x2 = (img->width) >> (img->res - 4 + shift); + } else { + x1 = img->left >> (img->res - 5 + shift); + x2 = (img->width) >> (img->res - 5 + shift); + } + for (x = 0; x < x1; x++) { + bit += bits[shiftreg]; + stream += bit >> 3, bit &= 7; + SETSHIFT; + } + for (x = 0; x < x2; x++) { + data[x] = LUT_range[RANGE + (int) data[x] + + (signed char) seq[shiftreg]]; + bit += bits[shiftreg]; + stream += bit >> 3, bit &= 7; + SETSHIFT; + } + } + } + return ((stream - start) + 0x6000 + 2047) & ~0x7ff; +} + +int +pcd_decode(struct PCD_IMAGE *img) +{ + int pos = HUFF1, rc; + + switch (img->res) { + case 1: + case 2: + case 3: + /* nothing to do */ + break; + case 4: + pcd_inter_m2(img); + + if (!img->gray) { + pcd_inter_lines(img->blue, img->width >> 1, img->height >> 1); + pcd_inter_lines(img->red, img->width >> 1, img->height >> 1); + } + pcd_inter_lines(img->luma, img->width, img->height); + if (-1 == (rc = pcd_read_htable(img->mmap + pos, &img->seq1, &img->len1))) + return -1; + pos += rc; + pos = (pos + 2047) & ~0x3ff; + if (-1 == pcd_un_huff(img, img->mmap + pos, 1)) + return (-1); + TELL('*'); + break; + case 5: + pcd_inter_m2(img); + + if (!img->gray) { + pcd_inter_lines(img->blue, img->width >> 2, img->height >> 2); + pcd_inter_pixels(img->blue, img->width >> 1, img->height >> 1); + pcd_inter_lines(img->blue, img->width >> 1, img->height >> 1); + pcd_inter_lines(img->red, img->width >> 2, img->height >> 2); + pcd_inter_pixels(img->red, img->width >> 1, img->height >> 1); + pcd_inter_lines(img->red, img->width >> 1, img->height >> 1); + } + pcd_inter_lines(img->luma, img->width >> 1, img->height >> 1); + if (-1 == (rc = pcd_read_htable(img->mmap + pos, &img->seq1, &img->len1))) + return -1; + pos += rc; + pos = (pos + 2047) & ~0x3ff; + if (-1 == (rc = pcd_un_huff(img, img->mmap + pos, 1))) + return (-1); + pos += rc; + TELL('*'); + pcd_inter_pixels(img->luma, img->width, img->height); + pcd_inter_lines(img->luma, img->width, img->height); + if (-1 == (rc = pcd_read_htable(img->mmap + pos, &img->seq1, &img->len1))) + return -1; + pos += rc; + if (-1 == (rc = pcd_read_htable(img->mmap + pos, &img->seq2, &img->len2))) + return -1; + pos += rc; + if (-1 == (rc = pcd_read_htable(img->mmap + pos, &img->seq3, &img->len3))) + return -1; + pos += rc; + pos = (pos + 2047) & ~0x3ff; + if (-1 == pcd_un_huff(img, img->mmap + pos, 2)) + return -1; + TELL('*'); + break; + default: + fprintf(stderr, "Oops: invalid res %i, have you called pcd_select()?\n", + img->res); + exit(1); + break; + } + return 0; +} @@ -0,0 +1,96 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pcd.h" + +int +pcd_inter_m2(struct PCD_IMAGE *img) +{ + register unsigned char *src, *dest; + register int x; + int y; + int left = img->left >> (img->res - 3); + int top = img->top >> (img->res - 3); + int width = img->width >> (img->res - 3); + int height = img->height >> (img->res - 3); + + src = img->mmap + pcd_img_start[3] + + (pcd_def_width[3] >> 1) * 3 * top; + + for (y = 0; y < height; y += 2) { + /* luma */ + src += left; + for (dest = img->luma[y << 1], x = 0; x < width - 1; x++) { + *(dest++) = src[x], *(dest++) = (src[x] + src[x + 1] + 1) >> 1; + } + *(dest++) = src[x], *(dest++) = src[x]; + src += pcd_def_width[3]; + for (dest = img->luma[(y << 1) + 2], x = 0; x < width - 1; x++) { + *(dest++) = src[x], *(dest++) = (src[x] + src[x + 1] + 1) >> 1; + } + *(dest++) = src[x], *(dest++) = src[x]; + src += pcd_def_width[3] - left; + + /* chroma */ + src += left >> 1; + for (dest = img->blue[y], x = 0; x < (width >> 1) - 1; x++) { + *(dest++) = src[x], *(dest++) = (src[x] + src[x + 1] + 1) >> 1; + } + *(dest++) = src[x], *(dest++) = src[x]; + src += pcd_def_width[3] >> 1; + for (dest = img->red[y], x = 0; x < (width >> 1) - 1; x++) { + *(dest++) = src[x], *(dest++) = (src[x] + src[x + 1] + 1) >> 1; + } + *(dest++) = src[x], *(dest++) = src[x]; + src += (pcd_def_width[3] - left) >> 1; + } + return 0; +} + +int +pcd_inter_pixels(unsigned char **data, int width, int height) +{ + register unsigned char *src, *dest; + register int x; + int y; + + for (y = height - 2; y >= 0; y -= 2) { + src = data[y >> 1]; + dest = data[y]; + dest[width - 2] = dest[width - 1] = src[(width >> 1) - 1]; + for (x = width - 4; x >= 0; x -= 2) { + dest[x] = src[x >> 1]; + dest[x + 1] = (src[x >> 1] + src[(x >> 1) + 1] + 1) >> 1; + } + } + return 0; +} + +int +pcd_inter_lines(unsigned char **data, int width, int height) +{ + register unsigned char *src1, *src2, *dest; + register int x; + int y; + + for (y = 0; y < height - 2; y += 2) { + src1 = data[y]; + dest = data[y + 1]; + src2 = data[y + 2]; + for (x = 0; x < width - 2; x += 2) { + dest[x] = (src1[x] + src2[x] + 1) >> 1; + dest[x + 1] = (src1[x] + src2[x] + src1[x + 2] + src2[x + 2] + 2) >> 2; + } + dest[x] = dest[x + 1] = (src1[x] + src2[x] + 1) >> 1; + } + src1 = data[y]; + dest = data[y + 1]; + for (x = 0; x < width - 2; x += 2) { + dest[x] = src1[x]; + dest[x + 1] = (src1[x] + src1[x + 2] + 1) >> 1; + } + dest[x] = dest[x + 1] = src1[x]; + return 0; +} @@ -0,0 +1,5 @@ +html { color: #000060 } +a { color: royalblue } +tt { color: black } +strong { color: red } +h1,h2,h3 { color: darkgreen } @@ -0,0 +1,88 @@ +#include <inttypes.h> + +struct PCD_IMAGE { + int size; + unsigned char *mmap; + int thumbnails; /* # of thumbnails, 0 for normal image */ + + int res, nr, gray, verbose; + int left, top, width, height, rot; + unsigned char **luma; + unsigned char **red; + unsigned char **blue; + unsigned char *data; + + uint32_t *lut_red; + uint32_t *lut_green; + uint32_t *lut_blue; + + unsigned char *seq1; /* huffman tables */ + unsigned char *len1; + unsigned char *seq2; + unsigned char *len2; + unsigned char *seq3; + unsigned char *len3; +}; + +/* --- file.c --- */ + +#define PCD_WIDTH(res,rot) (rot&1?pcd_def_height[res]:pcd_def_width[res]) +#define PCD_HEIGHT(res,rot) (rot&1?pcd_def_width[res]:pcd_def_height[res]) + +extern char pcd_rotor[]; +extern int pcd_img_start[]; +extern int pcd_def_width[]; +extern int pcd_def_height[]; +extern char pcd_errmsg[]; + +int pcd_open(struct PCD_IMAGE *img, char *filename); +int pcd_get_rot(struct PCD_IMAGE *img, int nr); +int pcd_get_maxres(struct PCD_IMAGE *img); +int pcd_select(struct PCD_IMAGE *img, int res, int nr, int gray, int verbose, + int rot, int *left, int *top, int *width, int *height); +int pcd_free(struct PCD_IMAGE *img); +int pcd_close(struct PCD_IMAGE *img); + +/* --- yuv2rgb.c --- */ +#define RANGE 320 +extern int32_t LUT_range[256 + 2 * RANGE]; + +extern uint32_t LUT_15_red[256]; +extern uint32_t LUT_15_green[256]; +extern uint32_t LUT_15_blue[256]; + +extern uint32_t LUT_16_red[256]; +extern uint32_t LUT_16_green[256]; +extern uint32_t LUT_16_blue[256]; + +extern uint32_t LUT_24_red[256]; +extern uint32_t LUT_24_green[256]; +extern uint32_t LUT_24_blue[256]; + +#define PCD_TYPE_GRAY 1 /* gray - 1 byte/pixel */ +#define PCD_TYPE_RGB 2 /* red,green,blue - 3 byte/pixel */ +#define PCD_TYPE_BGR 3 /* blue,green,red - 3 byte/pixel */ +#define PCD_TYPE_LUT_SHORT 4 /* lookup table - 2 byte/pixel */ +#define PCD_TYPE_LUT_LONG 5 /* lookup table - 4 byte/pixel */ + +void pcd_get_LUT_init(void); +void pcd_set_lookup(struct PCD_IMAGE *img, uint32_t *red, + uint32_t *green, uint32_t *blue); +int pcd_get_image_line(struct PCD_IMAGE *img, int line, + unsigned char *dest, int type, int scale); +int pcd_get_image(struct PCD_IMAGE *img, + unsigned char *dest, int type, int scale); + +/* --- huff.c --- */ +int pcd_read_htable(unsigned char *src, + unsigned char **pseq, unsigned char **pbits); +int pcd_decode(struct PCD_IMAGE *img); + +/* --- inter.c --- */ +int pcd_inter_m2(struct PCD_IMAGE *img); +int pcd_inter_pixels(unsigned char **data, int width, int height); +int pcd_inter_lines(unsigned char **data, int width, int height); + +/* ----------------------------------------------------------------- */ +#define TELL(x) { if(img->verbose) fputc(x,stderr); } +#define ROTOR(x) { if(img->verbose) fprintf(stderr,"%c\010",pcd_rotor[x&3]); } diff --git a/pcd.html b/pcd.html new file mode 100644 index 0000000..d504d58 --- /dev/null +++ b/pcd.html @@ -0,0 +1,51 @@ +<html> +<head> +<title>libpcd</title> +<link rel=stylesheet href=pcd.css> +</head> +<body> +<html> + +<h1>libpcd - a library for reading PhotoCD images</h1> + +<em>doc is really incomplete</em> + +<h2>1. Overview and basic functions</h2> +<dl> +<dt><tt>pcd_open</tt><dd>Open a file for reading +<dt><tt>pcd_select</tt><dd>Select resolution and area for decoding, set some +options. Allocates required memory. +<dt><tt>pcd_decode</tt><dd>Does the huffmann decoding (for 4BASE and 16BASE) +<dt><tt>pcd_get_image</tt><dd>Transforms the PhotoCD's YUV coding to +RGB/GRAY, various output formats are possible. +<dt><tt>pcd_get_image_line</tt><dd>dito, but allows reading the image +line by line. +<dt><tt>pcd_close</tt><dd>Close file. +</dl> +The <tt>pcd_select</tt>, <tt>pcd_decode</tt>, <tt>pcd_get_imgdata</tt> +cycle can run more than once with different parameters without +reopening the file. +<p> +There are few other functions and useful macros, see the Documentation +below for details. You may have a look to the test/example +applications too. + + +<h2>2. Detailed library description</h2> + +TODO + + +<h2>3. Library TODO List</h2> +<ul> +<li>write documentation +<li>add more sanity checks. Currently it is no problem to crash the +library. However, it should'nt happen for normal usage (i.e. calling +everything in order, don't try to read jpeg images etc...) +<li>provide error-codes (errno-like) +<li>maybe add 64BASE (6144x4096, PhotoCD Pro) resolution support +</ul> + + +</body> +</html> diff --git a/yuv2rgb.c b/yuv2rgb.c new file mode 100644 index 0000000..bc36a64 --- /dev/null +++ b/yuv2rgb.c @@ -0,0 +1,469 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pcd.h" + +/* ------------------------------------------------------------------------ */ + +#define RANGE 320 + +#define RED_NUL 137 +#define BLUE_NUL 156 + +#define LUN_MUL 360 +#define RED_MUL 512 +#define BLUE_MUL 512 +#define GREEN1_MUL (-RED_MUL/2) +#define GREEN2_MUL (-BLUE_MUL/6) + +#define RED_ADD (-RED_NUL*RED_MUL) +#define BLUE_ADD (-BLUE_NUL*BLUE_MUL) +#define GREEN1_ADD (-RED_ADD/2) +#define GREEN2_ADD (-BLUE_ADD/6) + +static int LUT_flag = 0; +static uint_fast16_t LUT_gray_char[256]; +static uint_fast16_t LUT_gray_int[256]; +static uint_fast16_t LUT_red[256]; +static uint_fast16_t LUT_blue[256]; +static uint_fast16_t LUT_green1[256]; +static uint_fast16_t LUT_green2[256]; + +int32_t LUT_range[256 + 2 * RANGE]; + +uint32_t LUT_15_red[256]; +uint32_t LUT_15_green[256]; +uint32_t LUT_15_blue[256]; + +uint32_t LUT_16_red[256]; +uint32_t LUT_16_green[256]; +uint32_t LUT_16_blue[256]; + +uint32_t LUT_24_red[256]; +uint32_t LUT_24_green[256]; +uint32_t LUT_24_blue[256]; + +/* ------------------------------------------------------------------------ */ + +void +pcd_get_LUT_init() +{ + register int i; + + /* only once needed */ + if (LUT_flag) + return; + LUT_flag = 1; + + /* init Lookup tables */ + for (i = 0; i < 256; i++) { + LUT_gray_int[i] = i * LUN_MUL >> 8; + LUT_red[i] = (RED_ADD + i * RED_MUL) >> 8; + LUT_blue[i] = (BLUE_ADD + i * BLUE_MUL) >> 8; + LUT_green1[i] = (GREEN1_ADD + i * GREEN1_MUL) >> 8; + LUT_green2[i] = (GREEN2_ADD + i * GREEN2_MUL) >> 8; + LUT_gray_char[i] = LUT_gray_int[i] > 255 ? 255 : LUT_gray_int[i]; + + LUT_15_red[i] = (i & 0xf8) << 7; /* bits -rrrrr-- -------- */ + LUT_15_green[i] = (i & 0xf8) << 2; /* bits------gg ggg----- */ + LUT_15_blue[i] = (i & 0xf8) >> 3; /* bits-------- ---bbbbb */ + + LUT_16_red[i] = (i & 0xf8) << 8; /* bits rrrrr--- -------- */ + LUT_16_green[i] = (i & 0xfc) << 3; /* bits -----ggg ggg----- */ + LUT_16_blue[i] = (i & 0xf8) >> 3; /* bits -------- ---bbbbb */ + + LUT_24_red[i] = i << 16; /* byte -r-- */ + LUT_24_green[i] = i << 8; /* byte --g- */ + LUT_24_blue[i] = i; /* byte ---b */ + } + for (i = 0; i < RANGE; i++) + LUT_range[i] = 0; + for (; i < RANGE + 256; i++) + LUT_range[i] = i - RANGE; + for (; i < 2 * RANGE + 256; i++) + LUT_range[i] = 255; +} + +void +pcd_set_lookup(struct PCD_IMAGE *img, uint32_t *red, + uint32_t *green, uint32_t *blue) +{ + img->lut_red = red; + img->lut_green = green; + img->lut_blue = blue; +} + +/* ------------------------------------------------------------------------ */ + +#define GET_RED (LUT_range[RANGE + gray + \ + LUT_red[red[x]]]) +#define GET_GREEN (LUT_range[RANGE + gray + \ + LUT_green1[red[x]] + \ + LUT_green2[blue[x]]]) +#define GET_BLUE (LUT_range[RANGE + gray + \ + LUT_blue[blue[x]]]) + +static int +pcd_get_image_line_0(struct PCD_IMAGE *img, int y, + unsigned char *dest, int type, int scale) +{ + unsigned char red[3072]; + unsigned char blue[3072]; + int bytes, maxx; + + switch (type) { + case PCD_TYPE_GRAY: + bytes = 1; + break; + case PCD_TYPE_RGB: + bytes = 3; + break; + case PCD_TYPE_BGR: + bytes = 3; + break; + case PCD_TYPE_LUT_SHORT: + bytes = 2; + break; + case PCD_TYPE_LUT_LONG: + bytes = 4; + break; + default: + fprintf(stderr, "Oops: invalid type (%i) for output format\n", type); + exit(1); + } + + if (img->rot & 2) { + y = (img->height >> scale) - y - 1; + dest += ((img->width >> scale) - 1) * bytes; + bytes = -bytes; + } + if (type != PCD_TYPE_GRAY && !scale) { + register int x; + register unsigned char *src1, *src2; + + maxx = (img->width >> 1) - 1; + if (y & 1) { + src1 = img->blue[y >> 1]; + src2 = img->blue[(y + 1 == img->height ? y : y + 1) >> 1]; + for (x = 0; x < maxx; x++) { + blue[x * 2] = (src1[x] + src2[x] + 1) >> 1; + blue[x * 2 + 1] = (src1[x] + src1[x + 1] + src2[x] + src2[x + 1] + 2) >> 2; + } + blue[x * 2 + 1] = blue[x * 2] = (src1[x] + src2[x] + 1) >> 1; + + src1 = img->red[y >> 1]; + src2 = img->red[(y + 1 == img->height ? y : y + 1) >> 1]; + for (x = 0; x < maxx; x++) { + red[x * 2] = (src1[x] + src2[x] + 1) >> 1; + red[x * 2 + 1] = (src1[x] + src1[x + 1] + src2[x] + src2[x + 1] + 2) >> 2; + } + red[x * 2 + 1] = red[x * 2] = (src1[x] + src2[x] + 1) >> 1; + } else { + src1 = img->blue[y >> 1]; + for (x = 0; x < maxx; x++) { + blue[x * 2] = src1[x]; + blue[x * 2 + 1] = (src1[x] + src1[x + 1] + 1) >> 1; + } + blue[x * 2 + 1] = blue[x * 2] = src1[x]; + + src1 = img->red[y >> 1]; + for (x = 0; x < maxx; x++) { + red[x * 2] = src1[x]; + red[x * 2 + 1] = (src1[x] + src1[x + 1] + 1) >> 1; + } + red[x * 2 + 1] = red[x * 2] = src1[x]; + } + } + if (type != PCD_TYPE_GRAY && scale) { + memcpy(blue, img->blue[y], img->width >> 1); + memcpy(red, img->red[y], img->width >> 1); + } + maxx = img->width >> scale; + switch (type) { + case PCD_TYPE_GRAY: + { + register int x; + register unsigned char *luma = img->luma[y << scale]; + + for (x = 0; x < maxx; x++, dest += bytes) + *dest = LUT_gray_char[luma[x << scale]]; + } + break; + case PCD_TYPE_RGB: + { + register int x, gray; + register unsigned char *luma = img->luma[y << scale]; + + for (x = 0; x < maxx; x++, dest += bytes) { + gray = LUT_gray_int[luma[x << scale]]; + dest[0] = GET_RED; + dest[1] = GET_GREEN; + dest[2] = GET_BLUE; + } + } + break; + case PCD_TYPE_BGR: + { + register int x, gray; + register unsigned char *luma = img->luma[y << scale]; + + for (x = 0; x < maxx; x++, dest += bytes) { + gray = LUT_gray_int[luma[x << scale]]; + dest[0] = GET_BLUE; + dest[1] = GET_GREEN; + dest[2] = GET_RED; + } + } + break; + case PCD_TYPE_LUT_SHORT: + { + register int x, gray; + register unsigned char *luma = img->luma[y << scale]; + uint32_t *lr = img->lut_red; + uint32_t *lg = img->lut_green; + uint32_t *lb = img->lut_blue; + + for (x = 0; x < maxx; x++, dest += bytes) { + gray = LUT_gray_int[luma[x << scale]]; + *((uint16_t *) dest) = + lr[GET_RED] | lg[GET_GREEN] | lb[GET_BLUE]; + } + } + break; + case PCD_TYPE_LUT_LONG: + { + register int x, gray; + register unsigned char *luma = img->luma[y << scale]; + uint32_t *lr = img->lut_red; + uint32_t *lg = img->lut_green; + uint32_t *lb = img->lut_blue; + + for (x = 0; x < maxx; x++, dest += bytes) { + gray = LUT_gray_int[luma[x << scale]]; + *((uint32_t *) dest) = + lr[GET_RED] | lg[GET_GREEN] | lb[GET_BLUE]; + } + } + break; + default: + exit(1); + } + return 0; +} + +static int +pcd_get_image_line_90(struct PCD_IMAGE *img, int y, + unsigned char *dest, int type, int scale) +{ + unsigned char red[3072]; + unsigned char blue[3072]; + unsigned char **luma = img->luma; + int bytes, maxx, y1, y2; + + switch (type) { + case PCD_TYPE_GRAY: + bytes = 1; + break; + case PCD_TYPE_RGB: + bytes = 3; + break; + case PCD_TYPE_BGR: + bytes = 3; + break; + case PCD_TYPE_LUT_SHORT: + bytes = 2; + break; + case PCD_TYPE_LUT_LONG: + bytes = 4; + break; + default: + fprintf(stderr, "Oops: invalid type (%i) for output format\n", type); + exit(1); + } + + if (!(img->rot & 2)) { + y = (img->width >> scale) - y - 1; + } else { + dest += ((img->height >> scale) - 1) * bytes; + bytes = -bytes; + } + + if (type != PCD_TYPE_GRAY && !scale) { + register int x; + register unsigned char **src; + + y1 = y >> 1; + y2 = (y + 1 == img->width ? y : y + 1) >> 1; + maxx = (img->height >> 1) - 1; + + if (y & 1) { + src = img->blue; + for (x = 0; x < maxx; x++) { + blue[x * 2] = (src[x][y1] + src[x][y2] + 1) >> 1; + blue[x * 2 + 1] = (src[x][y1] + src[x][y2] + + src[x + 1][y1] + src[x + 1][y2] + 2) >> 2; + } + blue[x * 2 + 1] = blue[x * 2] = (src[x][y1] + src[x][y2] + 1) >> 1; + + src = img->red; + for (x = 0; x < maxx; x++) { + red[x * 2] = (src[x][y1] + src[x][y2] + 1) >> 1; + red[x * 2 + 1] = (src[x][y1] + src[x][y2] + + src[x + 1][y1] + src[x + 1][y2] + 2) >> 2; + } + red[x * 2 + 1] = red[x * 2] = (src[x][y1] + src[x][y2] + 1) >> 1; + } else { + src = img->blue; + for (x = 0; x < maxx; x++) { + blue[x * 2] = src[x][y1]; + blue[x * 2 + 1] = (src[x][y1] + src[x + 1][y1] + 1) >> 1; + } + blue[x * 2 + 1] = blue[x * 2] = src[x][y1]; + + src = img->red; + for (x = 0; x < maxx; x++) { + red[x * 2] = src[x][y1]; + red[x * 2 + 1] = (src[x][y1] + src[x + 1][y1] + 1) >> 1; + } + red[x * 2 + 1] = red[x * 2] = src[x][y1]; + } + } + if (type != PCD_TYPE_GRAY && scale) { + register int x; + register unsigned char **src; + + maxx = (img->height >> 1); + + src = img->blue; + for (x = 0; x < maxx; x++) + blue[x] = src[x][y]; + + src = img->red; + for (x = 0; x < maxx; x++) + red[x] = src[x][y]; + } + maxx = (img->height >> scale); + switch (type) { + case PCD_TYPE_GRAY: + { + register int x; + + for (x = 0; x < maxx; x++, dest += bytes) + *dest = LUT_gray_char[luma[x << scale][y << scale]]; + } + break; + case PCD_TYPE_RGB: + { + register int x, gray; + + for (x = 0; x < maxx; x++, dest += bytes) { + gray = LUT_gray_int[luma[x << scale][y << scale]]; + dest[0] = GET_RED; + dest[1] = GET_GREEN; + dest[2] = GET_BLUE; + } + } + break; + case PCD_TYPE_BGR: + { + register int x, gray; + + for (x = 0; x < maxx; x++, dest += bytes) { + gray = LUT_gray_int[luma[x << scale][y << scale]]; + dest[0] = GET_BLUE; + dest[1] = GET_GREEN; + dest[2] = GET_RED; + } + } + break; + case PCD_TYPE_LUT_SHORT: + { + register int x, gray; + uint32_t *lr = img->lut_red; + uint32_t *lg = img->lut_green; + uint32_t *lb = img->lut_blue; + + for (x = 0; x < maxx; x++, dest += bytes) { + gray = LUT_gray_int[luma[x << scale][y << scale]]; + *((uint16_t *) dest) = + lr[GET_RED] | lg[GET_GREEN] | lb[GET_BLUE]; + } + } + break; + case PCD_TYPE_LUT_LONG: + { + register int x, gray; + uint32_t *lr = img->lut_red; + uint32_t *lg = img->lut_green; + uint32_t *lb = img->lut_blue; + + for (x = 0; x < maxx; x++, dest += bytes) { + gray = LUT_gray_int[luma[x << scale][y << scale]]; + *((uint32_t *) dest) = + lr[GET_RED] | lg[GET_GREEN] | lb[GET_BLUE]; + } + } + break; + default: + exit(1); + } + return 0; +} + +int +pcd_get_image_line(struct PCD_IMAGE *img, int y, + unsigned char *dest, int type, int scale) +{ + if (img->res == 0) { + fprintf(stderr, "Oops: invalid res %i, have you called pcd_select()?\n", + img->res); + exit(1); + } + if (img->rot & 1) + return pcd_get_image_line_90(img, y, dest, type, scale); + else + return pcd_get_image_line_0(img, y, dest, type, scale); +} + +int +pcd_get_image(struct PCD_IMAGE *img, unsigned char *dest, int type, int scale) +{ + int y, maxx, maxy, bytes; + + if (img->res == 0) { + fprintf(stderr, "Oops: invalid res %i, have you called pcd_select()?\n", + img->res); + exit(1); + } + switch (type) { + case PCD_TYPE_GRAY: + bytes = 1; + break; + case PCD_TYPE_RGB: + bytes = 3; + break; + case PCD_TYPE_BGR: + bytes = 3; + break; + case PCD_TYPE_LUT_SHORT: + bytes = 2; + break; + case PCD_TYPE_LUT_LONG: + bytes = 4; + break; + default: + fprintf(stderr, "Oops: invalid type (%i) for output format\n", type); + exit(1); + } + maxx = (img->rot & 1 ? img->height : img->width) >> scale; + maxy = (img->rot & 1 ? img->width : img->height) >> scale; + + for (y = 0; y < maxy; y++, dest += bytes * maxx) { + ROTOR(y); + pcd_get_image_line(img, y, dest, type, scale); + } + TELL('*'); + + return 0; +} |