summaryrefslogtreecommitdiffstats
path: root/src/imageutils
diff options
context:
space:
mode:
Diffstat (limited to 'src/imageutils')
-rw-r--r--src/imageutils/Makefile.am36
-rw-r--r--src/imageutils/README2
-rw-r--r--src/imageutils/asm_scale.S810
-rw-r--r--src/imageutils/croppedqimage.cpp77
-rw-r--r--src/imageutils/croppedqimage.h42
-rw-r--r--src/imageutils/imageutils.cpp211
-rw-r--r--src/imageutils/imageutils.h49
-rw-r--r--src/imageutils/jinclude.h91
-rw-r--r--src/imageutils/jpegcontent.cpp666
-rw-r--r--src/imageutils/jpegcontent.h86
-rw-r--r--src/imageutils/jpegerrormanager.h61
-rw-r--r--src/imageutils/jpegint.h392
-rw-r--r--src/imageutils/orient6.jpgbin0 -> 8828 bytes
-rw-r--r--src/imageutils/orientation.h58
-rw-r--r--src/imageutils/scale.cpp1979
-rw-r--r--src/imageutils/testjpegcontent.cpp256
-rw-r--r--src/imageutils/transupp.c928
-rw-r--r--src/imageutils/transupp.h141
18 files changed, 5885 insertions, 0 deletions
diff --git a/src/imageutils/Makefile.am b/src/imageutils/Makefile.am
new file mode 100644
index 0000000..33c83b3
--- /dev/null
+++ b/src/imageutils/Makefile.am
@@ -0,0 +1,36 @@
+# QT_CLEAN_NAMESPACE is needed when building with automake, otherwise
+# compilation fails in jpegcontent.cpp
+AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. $(LIBEXIF_CFLAGS) $(all_includes) \
+ -DQT_CLEAN_NAMESPACE
+AM_CCASFLAGS = -I$(srcdir) $(GV_ASM_DEFS)
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS)
+
+noinst_LTLIBRARIES = libgvimageutils.la
+
+libgvimageutils_la_SOURCES = \
+ imageutils.cpp \
+ jpegcontent.cpp \
+ scale.cpp \
+ transupp.c \
+ asm_scale.S \
+ croppedqimage.cpp
+
+libgvimageutils_la_LIBADD = $(LIB_KDECORE) $(LIBQT) $(LIBJPEG) $(LIB_EXIV2)
+
+noinst_HEADERS = \
+ orientation.h \
+ imageutils.h \
+ jpegcontent.h \
+ jinclude.h \
+ jpegint.h \
+ transupp.h \
+ jpegerrormanager.h \
+ croppedqimage.h
+
+METASOURCES = AUTO
+
+check_PROGRAMS = testjpegcontent
+testjpegcontent_SOURCES = testjpegcontent.cpp
+testjpegcontent_LDADD = $(LIB_KFILE) libgvimageutils.la
+testjpegcontent_LDFLAGS = $(all_libraries)
diff --git a/src/imageutils/README b/src/imageutils/README
new file mode 100644
index 0000000..335d36e
--- /dev/null
+++ b/src/imageutils/README
@@ -0,0 +1,2 @@
+This directory contains image utilities. It contains code from the JPEGLib to
+perform lossless transformations and from the ImageMagick library.
diff --git a/src/imageutils/asm_scale.S b/src/imageutils/asm_scale.S
new file mode 100644
index 0000000..08b43da
--- /dev/null
+++ b/src/imageutils/asm_scale.S
@@ -0,0 +1,810 @@
+#ifdef HAVE_X86_MMX
+
+#ifdef __EMX__
+/* Due to strange behaviour of as.exe we use this macros */
+/* For all OS/2 coders - please use PGCC to compile this code */
+#define PR_(foo) ___##foo
+#define PT_(foo,func) ___##foo,func
+#define SIZE(sym) \
+ .___end_##sym:; \
+ .size ___##sym,.___end_##sym-___##sym; \
+ .align 8;
+#else
+#define PR_(foo) __##foo
+#define PT_(foo,func) __##foo,func
+#define SIZE(sym) \
+ .__end_##sym:; \
+ .size __##sym,.__end_##sym-__##sym; \
+ .align 8;
+#endif
+
+/*\
+|*| MMX assembly scaling routine for Imlib2
+|*| Written by Willem Monsuwe <willem@stack.nl>
+\*/
+
+.text
+ .align 8
+.globl PR_(mimageScale_mmx_AARGBA)
+/* .type PT_(mimageScale_mmx_AARGBA,@function) */
+
+
+/*\ Prototype: __mimageScale_mmx_AARGBA(ImlibScaleInfo *isi, DATA32 *dest,
+|*| int dxx, int dyy, int dx, int dy, int dw, int dh, int dow, int sow)
+\*/
+
+#define isi 8(%ebp)
+#define dest 12(%ebp)
+#define dxx 16(%ebp)
+#define dyy 20(%ebp)
+#define dx 24(%ebp)
+#define dy 28(%ebp)
+#define dw 32(%ebp)
+#define dh 36(%ebp)
+#define dow 40(%ebp)
+#define sow 44(%ebp)
+
+/*\ Local variables that didn't fit in registers \*/
+#define y -4(%ebp)
+#define yp -8(%ebp)
+#define yap -12(%ebp)
+#define xp -16(%ebp)
+#define xap -20(%ebp)
+#define Cx -24(%ebp)
+#define Mx -28(%ebp)
+#define Cy -32(%ebp)
+#define My -36(%ebp)
+#define sow_4 -40(%ebp)
+
+/*\ When %edx points to ImlibScaleInfo, these are the members \*/
+#define xpoints (%edx)
+#define ypoints 4(%edx)
+#define xapoints 8(%edx)
+#define yapoints 12(%edx)
+#define xup_yup 16(%edx)
+
+PR_(mimageScale_mmx_AARGBA):
+ pushl %ebp
+ movl %esp, %ebp
+ subl $40, %esp
+ pushl %ebx
+ pushl %ecx
+ pushl %edx
+ pushl %edi
+ pushl %esi
+ movl isi, %edx
+
+ /*\ Check (dw > 0) && (dh > 0) \*/
+ cmpl $0, dw
+ jle .scale_leave
+ cmpl $0, dh
+ jle .scale_leave
+
+ /*\ X-based array pointers point to the end; we're looping up to 0 \*/
+ /*\ %edi = dest + dow * dy + dx + dw \*/
+ movl dow, %eax
+ imull dy, %eax
+ addl dx, %eax
+ addl dw, %eax
+ movl dest, %edi
+ leal (%edi, %eax, 4), %edi
+ /*\ xp = xpoints + dxx + dw \*/
+ movl dxx, %ebx
+ addl dw, %ebx
+ movl xpoints, %eax
+ leal (%eax, %ebx, 4), %eax
+ movl %eax, xp
+ /*\ xap = xapoints + dxx + dw \*/
+ movl xapoints, %eax
+ leal (%eax, %ebx, 4), %eax
+ movl %eax, xap
+ /*\ y = dh \*/
+ movl dh, %eax
+ movl %eax, y
+ /*\ yp = ypoints + dyy \*/
+ movl dyy, %ebx
+ movl ypoints, %eax
+ leal (%eax, %ebx, 4), %eax
+ movl %eax, yp
+ /*\ yap = yapoints + dyy \*/
+ movl yapoints, %eax
+ leal (%eax, %ebx, 4), %eax
+ movl %eax, yap
+
+ pxor %mm7, %mm7
+
+ /*\ Test xup bit \*/
+ movl xup_yup, %eax
+ sarl $1, %eax
+ jnc .scale_x_down
+
+.scale_x_up:
+ /*\ Test yup bit \*/
+ sarl $1, %eax
+ jnc .scale_x_up_y_down
+
+
+/*\ Scaling up both ways \*/
+
+.scale_x_up_y_up:
+ movl sow, %ebx
+
+.up_up_loop_y:
+
+ /*\ x = -dw \*/
+ movl dw, %ecx
+ negl %ecx
+
+ /*\ %eax = *yap << 4 \*/
+ movl yap, %eax
+ movl (%eax), %eax
+ sall $4, %eax
+ jz .up_up_yap_0
+ movd %eax, %mm1
+ punpcklwd %mm1, %mm1
+ punpckldq %mm1, %mm1
+
+.up_up_loop1_x:
+ /*\ %esi = *yp + xp[x] \*/
+ movl yp, %eax
+ movl (%eax), %esi
+ movl xp, %eax
+ movl (%eax, %ecx, 4), %eax
+ leal (%esi, %eax, 4), %esi
+
+ /*\ %eax = xap[x] << 4 \*/
+ movl xap, %eax
+ movl (%eax, %ecx, 4), %eax
+ sall $4, %eax
+ jz .up_up_xap_0
+
+ /*\ %mm0 = xap[x] << 4 \*/
+ movd %eax, %mm0
+ punpcklwd %mm0, %mm0
+ punpckldq %mm0, %mm0
+
+ /*\ Load and unpack four pixels in parralel
+ |*| %mm2 = ptr[0], %mm3 = ptr[1]
+ |*| %mm4 = ptr[sow], %mm5 = ptr[sow + 1]
+ \*/
+ movq (%esi), %mm2
+ movq (%esi, %ebx, 4), %mm4
+ movq %mm2, %mm3
+ movq %mm4, %mm5
+ punpcklbw %mm7, %mm2
+ punpcklbw %mm7, %mm4
+ punpckhbw %mm7, %mm3
+ punpckhbw %mm7, %mm5
+
+ /*\ X interpolation: r = l + (r - l) * xap \*/
+ psubw %mm2, %mm3
+ psubw %mm4, %mm5
+ psllw $4, %mm3
+ psllw $4, %mm5
+ pmulhw %mm0, %mm3
+ pmulhw %mm0, %mm5
+ paddw %mm2, %mm3
+ paddw %mm4, %mm5
+ /*\ Now %mm3 = I(ptr[0], ptr[1]), %mm5 = I(ptr[sow], ptr[sow + 1]) \*/
+ jmp .up_up_common
+.up_up_xap_0:
+ /*\ Load and unpack two pixels
+ |*| %mm3 = ptr[0], %mm5 = ptr[sow]
+ \*/
+ movd (%esi), %mm3
+ movd (%esi, %ebx, 4), %mm5
+ punpcklbw %mm7, %mm3
+ punpcklbw %mm7, %mm5
+.up_up_common:
+ /*\ Y interpolation: d = u + (d - u) * yap \*/
+ psubw %mm3, %mm5
+ psllw $4, %mm5
+ pmulhw %mm1, %mm5
+ paddw %mm3, %mm5
+ packuswb %mm5, %mm5
+ movd %mm5, (%edi, %ecx, 4)
+
+ /*\ while (++x) \*/
+ incl %ecx
+ jnz .up_up_loop1_x
+ jmp .up_up_yap_end
+.up_up_yap_0:
+
+.up_up_loop2_x:
+ /*\ %esi = *yp + xp[x] \*/
+ movl yp, %eax
+ movl (%eax), %esi
+ movl xp, %eax
+ movl (%eax, %ecx, 4), %eax
+ leal (%esi, %eax, 4), %esi
+
+ /*\ %eax = xap[x] << 4 \*/
+ movl xap, %eax
+ movl (%eax, %ecx, 4), %eax
+ sall $4, %eax
+ jz .up_up_0
+
+ /*\ %mm0 = xap[x] << 4 \*/
+ movd %eax, %mm0
+ punpcklwd %mm0, %mm0
+ punpckldq %mm0, %mm0
+
+ /*\ Load and unpack two pixels in parralel
+ |*| %mm2 = ptr[0], %mm3 = ptr[1]
+ \*/
+ movq (%esi), %mm2
+ movq %mm2, %mm3
+ punpcklbw %mm7, %mm2
+ punpckhbw %mm7, %mm3
+
+ /*\ X interpolation: r = l + (r - l) * xap \*/
+ psubw %mm2, %mm3
+ psllw $4, %mm3
+ pmulhw %mm0, %mm3
+ paddw %mm2, %mm3
+ packuswb %mm3, %mm3
+ movd %mm3, (%edi, %ecx, 4)
+ jmp .up_up_1
+.up_up_0:
+ /*\ dptr[x] = *sptr \*/
+ movl (%esi), %eax
+ movl %eax, (%edi, %ecx, 4)
+.up_up_1:
+ incl %ecx
+ jnz .up_up_loop2_x
+
+.up_up_yap_end:
+ /*\ dptr += dow \*/
+ movl dow, %eax
+ leal (%edi, %eax, 4), %edi
+ /*\ yap++; yp++ \*/
+ addl $4, yap
+ addl $4, yp
+ /*\ while (y--) \*/
+ decl y
+ jnz .up_up_loop_y
+
+ jmp .scale_leave
+
+
+/*\ Scaling down vertically \*/
+
+.scale_x_up_y_down:
+ /*\ sow_4 = sow * 4 \*/
+ movl sow, %eax
+ sall $2, %eax
+ movl %eax, sow_4
+
+.up_down_loop_y:
+
+ /*\ Setup My and Cy \*/
+ movl yap, %eax
+ movzwl (%eax), %ebx
+ movl %ebx, My
+ movzwl 2(%eax), %eax
+ movl %eax, Cy
+
+ /*\ mm4 = Cy \*/
+ movd %eax, %mm4
+ punpcklwd %mm4, %mm4
+ punpckldq %mm4, %mm4
+ /*\ mm5 = My \*/
+ movd %ebx, %mm5
+ punpcklwd %mm5, %mm5
+ punpckldq %mm5, %mm5
+
+ /*\ x = -dw \*/
+ movl dw, %ecx
+ negl %ecx
+.up_down_loop_x:
+ /*\ %esi = *yp + xp[x] \*/
+ movl yp, %eax
+ movl (%eax), %esi
+ movl xp, %eax
+ movl (%eax, %ecx, 4), %eax
+ leal (%esi, %eax, 4), %esi
+
+ movl %esi, %eax
+ /*\ v = (*p * My) >> 10 \*/
+ movd (%eax), %mm0
+ punpcklbw %mm7, %mm0
+ psllw $6, %mm0
+ pmulhw %mm5, %mm0
+
+ /*\ i = 0x4000 - My \*/
+ movl $0x4000, %ebx
+ subl My, %ebx
+ jbe 5f
+ jmp 2f
+1:
+ /*\ p += sow; v += (*p * Cy) >> 10 \*/
+ addl sow_4, %eax
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $6, %mm1
+ pmulhw %mm4, %mm1
+ paddw %mm1, %mm0
+
+ /*\ i -= Cy; while (i > Cy) \*/
+ subl Cy, %ebx
+2:
+ cmpl Cy, %ebx
+ jg 1b
+
+ /*\ mm6 = i \*/
+ movd %ebx, %mm6
+ punpcklwd %mm6, %mm6
+ punpckldq %mm6, %mm6
+
+ /*\ p += sow; v += (*p * i) >> 10 \*/
+ addl sow_4, %eax
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $6, %mm1
+ pmulhw %mm6, %mm1
+ paddw %mm1, %mm0
+5:
+ /*\ %eax = xap[x] << 5 \*/
+ movl xap, %eax
+ movl (%eax, %ecx, 4), %eax
+ sall $5, %eax
+ jz 6f
+ /*\ mm3 = xap[x] << 5 \*/
+ movd %eax, %mm3
+ punpcklwd %mm3, %mm3
+ punpckldq %mm3, %mm3
+
+ /*\ p + 1 \*/
+ movl %esi, %eax
+ addl $4, %eax
+ /*\ vv = (*p * My) >> 10 \*/
+ movd (%eax), %mm2
+ punpcklbw %mm7, %mm2
+ psllw $6, %mm2
+ pmulhw %mm5, %mm2
+
+ /*\ i = 0x4000 - My \*/
+ movl $0x4000, %ebx
+ subl My, %ebx
+ jbe 5f
+ jmp 2f
+1:
+ /*\ p += sow; vv += (*p * Cy) >> 10 \*/
+ addl sow_4, %eax
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $6, %mm1
+ pmulhw %mm4, %mm1
+ paddw %mm1, %mm2
+
+ /*\ i -= Cy; while (i > Cy) \*/
+ subl Cy, %ebx
+2:
+ cmpl Cy, %ebx
+ jg 1b
+
+ /*\ p += sow; v += (*p * i) >> 10 \*/
+ addl sow_4, %eax
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $6, %mm1
+ pmulhw %mm6, %mm1
+ paddw %mm1, %mm2
+5:
+ /*\ v = v + (vv - v) * xap \*/
+ psubw %mm0, %mm2
+ psllw $3, %mm2
+ pmulhw %mm3, %mm2
+ paddw %mm2, %mm0
+6:
+ /*\ dest[x] = v >> 4 \*/
+ psrlw $4, %mm0
+ packuswb %mm0, %mm0
+ movd %mm0, (%edi, %ecx, 4)
+
+ /*\ while (++x) \*/
+ incl %ecx
+ jnz .up_down_loop_x
+
+ /*\ dptr += dow \*/
+ movl dow, %eax
+ leal (%edi, %eax, 4), %edi
+ /*\ yap++; yp++ \*/
+ addl $4, yap
+ addl $4, yp
+ /*\ while (y--) \*/
+ decl y
+ jnz .up_down_loop_y
+
+ jmp .scale_leave
+
+.scale_x_down:
+ /*\ Test yup bit \*/
+ sarl $1, %eax
+ jnc .scale_x_down_y_down
+
+
+/*\ Scaling down horizontally \*/
+
+.scale_x_down_y_up:
+ /*\ sow_4 = sow * 4 \*/
+ movl sow, %eax
+ sall $2, %eax
+ movl %eax, sow_4
+
+.down_up_loop_y:
+
+ /*\ %eax = *yap << 5 \*/
+ movl yap, %eax
+ movl (%eax), %eax
+ sall $5, %eax
+ /*\ mm3 = *yap << 5 \*/
+ movd %eax, %mm3
+ punpcklwd %mm3, %mm3
+ punpckldq %mm3, %mm3
+
+ /*\ x = -dw \*/
+ movl dw, %ecx
+ negl %ecx
+.down_up_loop_x:
+ /*\ %esi = *yp + xp[x] \*/
+ movl yp, %eax
+ movl (%eax), %esi
+ movl xp, %eax
+ movl (%eax, %ecx, 4), %eax
+ leal (%esi, %eax, 4), %esi
+
+ /*\ Setup Mx and Cx \*/
+ movl xap, %eax
+ movzwl (%eax, %ecx, 4), %ebx
+ movl %ebx, Mx
+ movzwl 2(%eax, %ecx, 4), %eax
+ movl %eax, Cx
+
+ /*\ mm4 = Cx \*/
+ movd %eax, %mm4
+ punpcklwd %mm4, %mm4
+ punpckldq %mm4, %mm4
+ /*\ mm5 = Mx \*/
+ movd %ebx, %mm5
+ punpcklwd %mm5, %mm5
+ punpckldq %mm5, %mm5
+
+ movl %esi, %eax
+ /*\ v = (*p * Mx) >> 10 \*/
+ movd (%eax), %mm0
+ punpcklbw %mm7, %mm0
+ psllw $6, %mm0
+ pmulhw %mm5, %mm0
+
+ /*\ i = 0x4000 - Mx \*/
+ movl $0x4000, %ebx
+ subl Mx, %ebx
+ jbe 5f
+ jmp 2f
+1:
+ /*\ p += sow; v += (*p * Cx) >> 10 \*/
+ addl $4, %eax
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $6, %mm1
+ pmulhw %mm4, %mm1
+ paddw %mm1, %mm0
+
+ /*\ i -= Cx; while (i > Cx) \*/
+ subl Cx, %ebx
+2:
+ cmpl Cx, %ebx
+ jg 1b
+
+ /*\ mm6 = i \*/
+ movd %ebx, %mm6
+ punpcklwd %mm6, %mm6
+ punpckldq %mm6, %mm6
+
+ /*\ p += sow; v += (*p * i) >> 10 \*/
+ addl $4, %eax
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $6, %mm1
+ pmulhw %mm6, %mm1
+ paddw %mm1, %mm0
+5:
+ movd %mm3, %eax
+ testl %eax, %eax
+ jz 6f
+ /*\ p + sow \*/
+ movl %esi, %eax
+ addl sow_4, %eax
+ /*\ vv = (*p * Mx) >> 10 \*/
+ movd (%eax), %mm2
+ punpcklbw %mm7, %mm2
+ psllw $6, %mm2
+ pmulhw %mm5, %mm2
+
+ /*\ i = 0x4000 - Mx \*/
+ movl $0x4000, %ebx
+ subl Mx, %ebx
+ jbe 5f
+ jmp 2f
+1:
+ /*\ p += sow; vv += (*p * Cx) >> 10 \*/
+ addl $4, %eax
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $6, %mm1
+ pmulhw %mm4, %mm1
+ paddw %mm1, %mm2
+
+ /*\ i -= Cx; while (i > Cx) \*/
+ subl Cx, %ebx
+2:
+ cmpl Cx, %ebx
+ jg 1b
+
+ /*\ p += sow; v += (*p * i) >> 10 \*/
+ addl $4, %eax
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $6, %mm1
+ pmulhw %mm6, %mm1
+ paddw %mm1, %mm2
+5:
+ /*\ v = v + (vv - v) * yap \*/
+ psubw %mm0, %mm2
+ psllw $3, %mm2
+ pmulhw %mm3, %mm2
+ paddw %mm2, %mm0
+6:
+ /*\ dest[x] = v >> 4 \*/
+ psrlw $4, %mm0
+ packuswb %mm0, %mm0
+ movd %mm0, (%edi, %ecx, 4)
+
+ /*\ while (++x) \*/
+ incl %ecx
+ jnz .down_up_loop_x
+
+ /*\ dptr += dow \*/
+ movl dow, %eax
+ leal (%edi, %eax, 4), %edi
+ /*\ yap++; yp++ \*/
+ addl $4, yap
+ addl $4, yp
+ /*\ while (y--) \*/
+ decl y
+ jnz .down_up_loop_y
+
+ jmp .scale_leave
+
+
+/*\ Scaling down both ways \*/
+
+.scale_x_down_y_down:
+ /*\ sow_4 = sow * 4 \*/
+ movl sow, %eax
+ sall $2, %eax
+ movl %eax, sow_4
+
+.down_down_loop_y:
+
+ /*\ Setup My and Cy \*/
+ movl yap, %eax
+ movzwl (%eax), %ebx
+ movl %ebx, My
+ movzwl 2(%eax), %eax
+ movl %eax, Cy
+
+ /*\ x = -dw \*/
+ movl dw, %ecx
+ negl %ecx
+.down_down_loop_x:
+ /*\ %esi = *yp + xp[x] \*/
+ movl yp, %eax
+ movl (%eax), %esi
+ movl xp, %eax
+ movl (%eax, %ecx, 4), %eax
+ leal (%esi, %eax, 4), %esi
+
+ /*\ Setup Mx and Cx \*/
+ movl xap, %eax
+ movzwl (%eax, %ecx, 4), %ebx
+ movl %ebx, Mx
+ movzwl 2(%eax, %ecx, 4), %eax
+ movl %eax, Cx
+
+ /*\ mm3 = Cx \*/
+ movd %eax, %mm3
+ punpcklwd %mm3, %mm3
+ punpckldq %mm3, %mm3
+ /*\ mm5 = Mx \*/
+ movd %ebx, %mm5
+ punpcklwd %mm5, %mm5
+ punpckldq %mm5, %mm5
+
+ /*\ p = sptr; v = (*p * Mx) >> 9 \*/
+ movl %esi, %eax
+ movd (%eax), %mm0
+ punpcklbw %mm7, %mm0
+ psllw $7, %mm0
+ pmulhw %mm5, %mm0
+
+ /*\ i = 0x4000 - Mx \*/
+ movl $0x4000, %ebx
+ subl Mx, %ebx
+ jbe 5f
+ jmp 2f
+1:
+ /*\ v += (*++p * Cx) >> 9 \*/
+ addl $4, %eax
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $7, %mm1
+ pmulhw %mm3, %mm1
+ paddw %mm1, %mm0
+
+ /*\ i -= Cx; while (i > Cx) \*/
+ subl Cx, %ebx
+2:
+ cmpl Cx, %ebx
+ jg 1b
+
+ /*\ mm6 = i \*/
+ movd %ebx, %mm6
+ punpcklwd %mm6, %mm6
+ punpckldq %mm6, %mm6
+
+ /*\ v += (*++p * i) >> 9 \*/
+ addl $4, %eax
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $7, %mm1
+ pmulhw %mm6, %mm1
+ paddw %mm1, %mm0
+5:
+ /*\ v *= My \*/
+ movd My, %mm4
+ punpcklwd %mm4, %mm4
+ punpckldq %mm4, %mm4
+ psllw $2, %mm0
+ pmulhw %mm4, %mm0
+
+ /*\ j = 0x4000 - My \*/
+ movl $0x4000, %edx
+ subl My, %edx
+ jbe 6f
+ jmp 4f
+3:
+ /*\ sptr += sow; p = sptr \*/
+ addl sow_4, %esi
+ movl %esi, %eax
+ /*\ vx = (*p * Mx) >> 9 \*/
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $7, %mm1
+ pmulhw %mm5, %mm1
+
+ /*\ i = 0x4000 - Mx \*/
+ movl $0x4000, %ebx
+ subl Mx, %ebx
+ jbe 5f
+ jmp 2f
+1:
+ /*\ vx += (*++p * Cx) >> 9 \*/
+ addl $4, %eax
+ movd (%eax), %mm2
+ punpcklbw %mm7, %mm2
+ psllw $7, %mm2
+ pmulhw %mm3, %mm2
+ paddw %mm2, %mm1
+
+ /*\ i -= Cx; while (i > Cx) \*/
+ subl Cx, %ebx
+2:
+ cmpl Cx, %ebx
+ jg 1b
+
+ /*\ vx += (*++p * i) >> 9 \*/
+ addl $4, %eax
+ movd (%eax), %mm2
+ punpcklbw %mm7, %mm2
+ psllw $7, %mm2
+ pmulhw %mm6, %mm2
+ paddw %mm2, %mm1
+5:
+ /*\ v += (vx * Cy) >> 14 \*/
+ movd Cy, %mm4
+ punpcklwd %mm4, %mm4
+ punpckldq %mm4, %mm4
+ psllw $2, %mm1
+ pmulhw %mm4, %mm1
+ paddw %mm1, %mm0
+
+ /*\ j -= Cy; while (j > Cy) \*/
+ subl Cy, %edx
+4:
+ cmpl Cy, %edx
+ jg 3b
+
+ /*\ sptr += sow; p = sptr \*/
+ addl sow_4, %esi
+ movl %esi, %eax
+ /*\ vx = (*p * Mx) >> 9 \*/
+ movd (%eax), %mm1
+ punpcklbw %mm7, %mm1
+ psllw $7, %mm1
+ pmulhw %mm5, %mm1
+
+ /*\ i = 0x4000 - Mx \*/
+ movl $0x4000, %ebx
+ subl Mx, %ebx
+ jbe 5f
+ jmp 2f
+1:
+ /*\ vx += (*++p * Cx) >> 9 \*/
+ addl $4, %eax
+ movd (%eax), %mm2
+ punpcklbw %mm7, %mm2
+ psllw $7, %mm2
+ pmulhw %mm3, %mm2
+ paddw %mm2, %mm1
+
+ /*\ i -= Cx; while (i > Cx) \*/
+ subl Cx, %ebx
+2:
+ cmpl Cx, %ebx
+ jg 1b
+
+ /*\ vx += (*++p * i) >> 9 \*/
+ addl $4, %eax
+ movd (%eax), %mm2
+ punpcklbw %mm7, %mm2
+ psllw $7, %mm2
+ pmulhw %mm6, %mm2
+ paddw %mm2, %mm1
+5:
+ /*\ v += (vx * j) >> 14 \*/
+ movd %edx, %mm4
+ punpcklwd %mm4, %mm4
+ punpckldq %mm4, %mm4
+ psllw $2, %mm1
+ pmulhw %mm4, %mm1
+ paddw %mm1, %mm0
+6:
+ /*\ dptr[x] = mm0 >> 5 \*/
+ psrlw $5, %mm0
+ packuswb %mm0, %mm0
+ movd %mm0, (%edi, %ecx, 4)
+
+ /*\ while (++x) \*/
+ incl %ecx
+ jnz .down_down_loop_x
+
+ /*\ dptr += dow \*/
+ movl dow, %eax
+ leal (%edi, %eax, 4), %edi
+ /*\ yap++; yp++ \*/
+ addl $4, yap
+ addl $4, yp
+ /*\ while (y--) \*/
+ decl y
+ jnz .down_down_loop_y
+
+ jmp .scale_leave
+
+.scale_leave:
+ emms
+ popl %esi
+ popl %edi
+ popl %edx
+ popl %ecx
+ popl %ebx
+ movl %ebp, %esp
+ popl %ebp
+ ret
+
+SIZE(mimageScale_mmx_AARGBA)
+
+#endif
+
+.section .note.GNU-stack,"",%progbits
diff --git a/src/imageutils/croppedqimage.cpp b/src/imageutils/croppedqimage.cpp
new file mode 100644
index 0000000..5ed2aab
--- /dev/null
+++ b/src/imageutils/croppedqimage.cpp
@@ -0,0 +1,77 @@
+/*
+
+ Copyright (C) 2005 Lubos Lunak <l.lunak@kde.org>
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "croppedqimage.h"
+
+namespace ImageUtils
+{
+
+// This class is used in ImageView::performPaint(). Just using QImage::copy( QRect )
+// takes a significant time with very large images. So instead of copying the image data
+// just create CroppedQImage which fakes a subimage by manipulating its scanline pointers.
+// That will of course break if something doesn't use scanlines but accesses the image
+// data directly, QImage::copy() being the most notable case. There are two ways
+// to handle that: 1) It is possible to manually call normalize() which will make
+// CroppedQImage copy the image data and own it, just like proper QImage. 2) CroppedQImage
+// has as a data member also QImage holding the original image. This ensures that all
+// the original image data are still available for the whole lifetime of CroppedQImage.
+
+CroppedQImage::CroppedQImage( const QImage& im, const QRect& rect )
+ : QImage( rect.size(), im.depth(), im.numColors(), im.bitOrder())
+ , orig( im )
+ {
+ if( im.isNull())
+ return;
+ memcpy( colorTable(), im.colorTable(), im.numColors() * sizeof( QRgb ));
+ setAlphaBuffer( im.hasAlphaBuffer());
+ setDotsPerMeterX( im.dotsPerMeterX());
+ setDotsPerMeterY( im.dotsPerMeterY());
+ //data->offset = im.offset();
+ // make scanlines point to right places in the original QImage
+ for( int i = 0;
+ i < height();
+ ++i )
+ jumpTable()[ i ] = im.scanLine( rect.y() + i ) + rect.x() * ( depth() / 8 );
+ }
+
+CroppedQImage& CroppedQImage::operator= ( const QImage& im )
+ {
+ QImage::operator=( im );
+ return *this;
+ }
+
+void CroppedQImage::normalize()
+ {
+ // is it a normal QImage with its own data?
+ uchar* firstdata = ( uchar* )( jumpTable() + height());
+ if( scanLine( 0 ) == firstdata )
+ return;
+ // copy the image data to our own data and make scanlines point properly there
+ for( int i = 0;
+ i < height();
+ ++i )
+ {
+ uchar* oldline = scanLine( i );
+ jumpTable()[ i ] = firstdata + i * bytesPerLine();
+ memcpy( scanLine( i ), oldline, bytesPerLine());
+ }
+ }
+
+} // namespace
diff --git a/src/imageutils/croppedqimage.h b/src/imageutils/croppedqimage.h
new file mode 100644
index 0000000..4d93840
--- /dev/null
+++ b/src/imageutils/croppedqimage.h
@@ -0,0 +1,42 @@
+/*
+
+ Copyright (C) 2005 Lubos Lunak <l.lunak@kde.org>
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef CROPPED_QIMAGE_H
+#define CROPPED_QIMAGE_H
+
+#include <qimage.h>
+
+namespace ImageUtils
+{
+
+class CroppedQImage
+ : public QImage
+ {
+ public:
+ CroppedQImage( const QImage& im, const QRect& rect );
+ CroppedQImage& operator= ( const QImage& im );
+ void normalize();
+ private:
+ QImage orig;
+ };
+
+} // namespace
+
+#endif
diff --git a/src/imageutils/imageutils.cpp b/src/imageutils/imageutils.cpp
new file mode 100644
index 0000000..c9d04ca
--- /dev/null
+++ b/src/imageutils/imageutils.cpp
@@ -0,0 +1,211 @@
+// vim: set tabstop=4 shiftwidth=4 noexpandtab
+/*
+Gwenview - A simple image viewer for KDE
+Copyright 2000-2004 Aurélien Gâteau
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#include <math.h>
+
+// Qt
+#include <qimage.h>
+#include <qwmatrix.h>
+
+// KDE
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kglobal.h>
+
+// Local
+#include "imageutils/orientation.h"
+
+namespace ImageUtils {
+
+
+QWMatrix transformMatrix(Orientation orientation) {
+ QWMatrix matrix;
+ switch (orientation) {
+ case NOT_AVAILABLE:
+ case NORMAL:
+ break;
+
+ case HFLIP:
+ matrix.scale(-1,1);
+ break;
+
+ case ROT_180:
+ matrix.rotate(180);
+ break;
+
+ case VFLIP:
+ matrix.scale(1,-1);
+ break;
+
+ case TRANSPOSE:
+ matrix.scale(-1,1);
+ matrix.rotate(90);
+ break;
+
+ case ROT_90:
+ matrix.rotate(90);
+ break;
+
+ case TRANSVERSE:
+ matrix.scale(1,-1);
+ matrix.rotate(90);
+ break;
+
+ case ROT_270:
+ matrix.rotate(270);
+ break;
+ }
+
+ return matrix;
+}
+
+
+QImage transform(const QImage& img, Orientation orientation) {
+ if (orientation != NOT_AVAILABLE && orientation != NORMAL) {
+ return img.xForm(transformMatrix(orientation));
+ } else {
+ return img;
+ }
+}
+
+
+inline
+int changeBrightness( int value, int brightness )
+ {
+ return KCLAMP( value + brightness * 255 / 100, 0, 255 );
+ }
+
+inline
+int changeContrast( int value, int contrast )
+ {
+ return KCLAMP((( value - 127 ) * contrast / 100 ) + 127, 0, 255 );
+ }
+
+inline
+int changeGamma( int value, int gamma )
+ {
+ return KCLAMP( int( pow( value / 255.0, 100.0 / gamma ) * 255 ), 0, 255 );
+ }
+
+inline
+int changeUsingTable( int value, const int table[] )
+ {
+ return table[ value ];
+ }
+
+/*
+ Applies either brightness, contrast or gamma conversion on the image.
+ If the image is not truecolor, the color table is changed. If it is
+ truecolor, every pixel has to be changed. In order to make it as fast
+ as possible, alpha value is converted only if necessary. Additionally,
+ since color components (red/green/blue/alpha) can have only 256 values
+ but images usually have many pixels, a conversion table is first
+ created for every color component value, and pixels are converted
+ using this table.
+*/
+
+template< int operation( int, int ) >
+static
+QImage changeImage( const QImage& image, int value )
+ {
+ QImage im = image;
+ im.detach();
+ if( im.numColors() == 0 ) /* truecolor */
+ {
+ if( im.depth() != 32 ) /* just in case */
+ im = im.convertDepth( 32 );
+ int table[ 256 ];
+ for( int i = 0;
+ i < 256;
+ ++i )
+ table[ i ] = operation( i, value );
+ if( im.hasAlphaBuffer())
+ {
+ for( int y = 0;
+ y < im.height();
+ ++y )
+ {
+ QRgb* line = reinterpret_cast< QRgb* >( im.scanLine( y ));
+ for( int x = 0;
+ x < im.width();
+ ++x )
+ line[ x ] = qRgba( changeUsingTable( qRed( line[ x ] ), table ),
+ changeUsingTable( qGreen( line[ x ] ), table ),
+ changeUsingTable( qBlue( line[ x ] ), table ),
+ changeUsingTable( qAlpha( line[ x ] ), table ));
+ }
+ }
+ else
+ {
+ for( int y = 0;
+ y < im.height();
+ ++y )
+ {
+ QRgb* line = reinterpret_cast< QRgb* >( im.scanLine( y ));
+ for( int x = 0;
+ x < im.width();
+ ++x )
+ line[ x ] = qRgb( changeUsingTable( qRed( line[ x ] ), table ),
+ changeUsingTable( qGreen( line[ x ] ), table ),
+ changeUsingTable( qBlue( line[ x ] ), table ));
+ }
+ }
+ }
+ else
+ {
+ QRgb* colors = im.colorTable();
+ for( int i = 0;
+ i < im.numColors();
+ ++i )
+ colors[ i ] = qRgb( operation( qRed( colors[ i ] ), value ),
+ operation( qGreen( colors[ i ] ), value ),
+ operation( qBlue( colors[ i ] ), value ));
+ }
+ return im;
+ }
+
+
+// brightness is multiplied by 100 in order to avoid floating point numbers
+QImage changeBrightness( const QImage& image, int brightness )
+ {
+ if( brightness == 0 ) // no change
+ return image;
+ return changeImage< changeBrightness >( image, brightness );
+ }
+
+
+// contrast is multiplied by 100 in order to avoid floating point numbers
+QImage changeContrast( const QImage& image, int contrast )
+ {
+ if( contrast == 100 ) // no change
+ return image;
+ return changeImage< changeContrast >( image, contrast );
+ }
+
+// gamma is multiplied by 100 in order to avoid floating point numbers
+QImage changeGamma( const QImage& image, int gamma )
+ {
+ if( gamma == 100 ) // no change
+ return image;
+ return changeImage< changeGamma >( image, gamma );
+ }
+
+} // Namespace
+
diff --git a/src/imageutils/imageutils.h b/src/imageutils/imageutils.h
new file mode 100644
index 0000000..62b757c
--- /dev/null
+++ b/src/imageutils/imageutils.h
@@ -0,0 +1,49 @@
+// vim: set tabstop=4 shiftwidth=4 noexpandtab
+/*
+Gwenview - A simple image viewer for KDE
+Copyright 2000-2004 Aurélien Gâteau
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#ifndef IMAGEUTILS_H
+#define IMAGEUTILS_H
+
+// Qt
+#include <qimage.h>
+
+// Local
+#include "imageutils/orientation.h"
+
+namespace ImageUtils {
+ enum SmoothAlgorithm { SMOOTH_NONE, SMOOTH_FAST, SMOOTH_NORMAL, SMOOTH_BEST };
+
+ QImage scale(const QImage& image, int width, int height,
+ SmoothAlgorithm alg, QImage::ScaleMode mode = QImage::ScaleFree, double blur = 1.0);
+
+ int extraScalePixels( SmoothAlgorithm alg, double zoom, double blur = 1.0 );
+
+ QImage transform(const QImage& img, Orientation orientation);
+
+ QImage changeBrightness( const QImage& image, int brightness );
+
+ QImage changeContrast( const QImage& image, int contrast );
+
+ QImage changeGamma( const QImage& image, int gamma );
+
+ QWMatrix transformMatrix(Orientation orientation);
+}
+
+#endif
diff --git a/src/imageutils/jinclude.h b/src/imageutils/jinclude.h
new file mode 100644
index 0000000..0a4f151
--- /dev/null
+++ b/src/imageutils/jinclude.h
@@ -0,0 +1,91 @@
+/*
+ * jinclude.h
+ *
+ * Copyright (C) 1991-1994, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file exists to provide a single place to fix any problems with
+ * including the wrong system include files. (Common problems are taken
+ * care of by the standard jconfig symbols, but on really weird systems
+ * you may have to edit this file.)
+ *
+ * NOTE: this file is NOT intended to be included by applications using the
+ * JPEG library. Most applications need only include jpeglib.h.
+ */
+
+
+/* Include auto-config file to find out which system include files we need. */
+
+#include "jconfig.h" /* auto configuration options */
+#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */
+
+/*
+ * We need the NULL macro and size_t typedef.
+ * On an ANSI-conforming system it is sufficient to include <stddef.h>.
+ * Otherwise, we get them from <stdlib.h> or <stdio.h>; we may have to
+ * pull in <sys/types.h> as well.
+ * Note that the core JPEG library does not require <stdio.h>;
+ * only the default error handler and data source/destination modules do.
+ * But we must pull it in because of the references to FILE in jpeglib.h.
+ * You can remove those references if you want to compile without <stdio.h>.
+ */
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef NEED_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+
+/*
+ * We need memory copying and zeroing functions, plus strncpy().
+ * ANSI and System V implementations declare these in <string.h>.
+ * BSD doesn't have the mem() functions, but it does have bcopy()/bzero().
+ * Some systems may declare memset and memcpy in <memory.h>.
+ *
+ * NOTE: we assume the size parameters to these functions are of type size_t.
+ * Change the casts in these macros if not!
+ */
+
+#ifdef NEED_BSD_STRINGS
+
+#include <strings.h>
+#define MEMZERO(target,size) bzero((void *)(target), (size_t)(size))
+#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size))
+
+#else /* not BSD, assume ANSI/SysV string lib */
+
+#include <string.h>
+#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size))
+#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size))
+
+#endif
+
+/*
+ * In ANSI C, and indeed any rational implementation, size_t is also the
+ * type returned by sizeof(). However, it seems there are some irrational
+ * implementations out there, in which sizeof() returns an int even though
+ * size_t is defined as long or unsigned long. To ensure consistent results
+ * we always use this SIZEOF() macro in place of using sizeof() directly.
+ */
+
+#define SIZEOF(object) ((size_t) sizeof(object))
+
+/*
+ * The modules that use fread() and fwrite() always invoke them through
+ * these macros. On some systems you may need to twiddle the argument casts.
+ * CAUTION: argument order is different from underlying functions!
+ */
+
+#define JFREAD(file,buf,sizeofbuf) \
+ ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file)))
+#define JFWRITE(file,buf,sizeofbuf) \
+ ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file)))
diff --git a/src/imageutils/jpegcontent.cpp b/src/imageutils/jpegcontent.cpp
new file mode 100644
index 0000000..b44b14d
--- /dev/null
+++ b/src/imageutils/jpegcontent.cpp
@@ -0,0 +1,666 @@
+// vim: set tabstop=4 shiftwidth=4 noexpandtab:
+/*
+Gwenview - A simple image viewer for KDE
+Copyright 2000-2004 Aurélien Gâteau
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// System
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+extern "C" {
+#include <jpeglib.h>
+#include "transupp.h"
+}
+
+// Qt
+#include <qbuffer.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qmap.h>
+#include <qwmatrix.h>
+
+// KDE
+#include <kdebug.h>
+
+// Exiv2
+#include <exiv2/exif.hpp>
+#include <exiv2/image.hpp>
+
+// Local
+#include "imageutils/imageutils.h"
+#include "imageutils/jpegcontent.h"
+#include "imageutils/jpegerrormanager.h"
+
+namespace ImageUtils {
+
+const int INMEM_DST_DELTA=4096;
+
+
+//------------------------------------------
+//
+// In-memory data source manager for libjpeg
+//
+//------------------------------------------
+struct inmem_src_mgr : public jpeg_source_mgr {
+ QByteArray* mInput;
+};
+
+void inmem_init_source(j_decompress_ptr cinfo) {
+ inmem_src_mgr* src=(inmem_src_mgr*)(cinfo->src);
+ src->next_input_byte=(const JOCTET*)( src->mInput->data() );
+ src->bytes_in_buffer=src->mInput->size();
+}
+
+/**
+ * If this function is called, it means the JPEG file is broken. We feed the
+ * decoder with fake EOI has specified in the libjpeg documentation.
+ */
+int inmem_fill_input_buffer(j_decompress_ptr cinfo) {
+ static JOCTET fakeEOI[2]={ JOCTET(0xFF), JOCTET(JPEG_EOI)};
+ kdWarning() << k_funcinfo << " Image is incomplete" << endl;
+ cinfo->src->next_input_byte=fakeEOI;
+ cinfo->src->bytes_in_buffer=2;
+ return true;
+}
+
+void inmem_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+ if (num_bytes<=0) return;
+ Q_ASSERT(num_bytes>=long(cinfo->src->bytes_in_buffer));
+ cinfo->src->next_input_byte+=num_bytes;
+ cinfo->src->bytes_in_buffer-=num_bytes;
+}
+
+void inmem_term_source(j_decompress_ptr /*cinfo*/) {
+}
+
+
+//-----------------------------------------------
+//
+// In-memory data destination manager for libjpeg
+//
+//-----------------------------------------------
+struct inmem_dest_mgr : public jpeg_destination_mgr {
+ QByteArray* mOutput;
+
+ void dump() {
+ kdDebug() << "dest_mgr:\n";
+ kdDebug() << "- next_output_byte: " << next_output_byte << endl;
+ kdDebug() << "- free_in_buffer: " << free_in_buffer << endl;
+ kdDebug() << "- output size: " << mOutput->size() << endl;
+ }
+};
+
+void inmem_init_destination(j_compress_ptr cinfo) {
+ inmem_dest_mgr* dest=(inmem_dest_mgr*)(cinfo->dest);
+ if (dest->mOutput->size()==0) {
+ bool result=dest->mOutput->resize(INMEM_DST_DELTA);
+ Q_ASSERT(result);
+ }
+ dest->free_in_buffer=dest->mOutput->size();
+ dest->next_output_byte=(JOCTET*)(dest->mOutput->data() );
+}
+
+int inmem_empty_output_buffer(j_compress_ptr cinfo) {
+ inmem_dest_mgr* dest=(inmem_dest_mgr*)(cinfo->dest);
+ bool result=dest->mOutput->resize(dest->mOutput->size() + INMEM_DST_DELTA);
+ Q_ASSERT(result);
+ dest->next_output_byte=(JOCTET*)( dest->mOutput->data() + dest->mOutput->size() - INMEM_DST_DELTA );
+ dest->free_in_buffer=INMEM_DST_DELTA;
+
+ return true;
+}
+
+void inmem_term_destination(j_compress_ptr cinfo) {
+ inmem_dest_mgr* dest=(inmem_dest_mgr*)(cinfo->dest);
+ int finalSize=dest->next_output_byte - (JOCTET*)(dest->mOutput->data());
+ Q_ASSERT(finalSize>=0);
+ dest->mOutput->resize(finalSize);
+}
+
+
+//---------------------
+//
+// JPEGContent::Private
+//
+//---------------------
+struct JPEGContent::Private {
+ QByteArray mRawData;
+ QSize mSize;
+ QString mComment;
+ QString mAperture;
+ QString mExposureTime;
+ QString mFocalLength;
+ QString mIso;
+
+ bool mPendingTransformation;
+ QWMatrix mTransformMatrix;
+ Exiv2::ExifData mExifData;
+
+ Private() {
+ mPendingTransformation = false;
+ }
+
+ void setupInmemSource(j_decompress_ptr cinfo) {
+ Q_ASSERT(!cinfo->src);
+ inmem_src_mgr* src = (inmem_src_mgr*)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+ sizeof(inmem_src_mgr));
+ cinfo->src=(struct jpeg_source_mgr*)(src);
+
+ src->init_source=inmem_init_source;
+ src->fill_input_buffer=inmem_fill_input_buffer;
+ src->skip_input_data=inmem_skip_input_data;
+ src->resync_to_restart=jpeg_resync_to_restart;
+ src->term_source=inmem_term_source;
+
+ src->mInput=&mRawData;
+ }
+
+
+ void setupInmemDestination(j_compress_ptr cinfo, QByteArray* outputData) {
+ Q_ASSERT(!cinfo->dest);
+ inmem_dest_mgr* dest = (inmem_dest_mgr*)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+ sizeof(inmem_dest_mgr));
+ cinfo->dest=(struct jpeg_destination_mgr*)(dest);
+
+ dest->init_destination=inmem_init_destination;
+ dest->empty_output_buffer=inmem_empty_output_buffer;
+ dest->term_destination=inmem_term_destination;
+
+ dest->mOutput=outputData;
+ }
+ bool readSize() {
+ struct jpeg_decompress_struct srcinfo;
+ jpeg_saved_marker_ptr mark;
+
+ // Init JPEG structs
+ JPEGErrorManager errorManager;
+
+ // Initialize the JPEG decompression object
+ srcinfo.err = &errorManager;
+ jpeg_create_decompress(&srcinfo);
+ if (setjmp(errorManager.jmp_buffer)) {
+ kdError() << k_funcinfo << "libjpeg fatal error\n";
+ return false;
+ }
+
+ // Specify data source for decompression
+ setupInmemSource(&srcinfo);
+
+ // Read the header
+ jcopy_markers_setup(&srcinfo, JCOPYOPT_ALL);
+ int result=jpeg_read_header(&srcinfo, true);
+ if (result!=JPEG_HEADER_OK) {
+ kdError() << "Could not read jpeg header\n";
+ jpeg_destroy_decompress(&srcinfo);
+ return false;
+ }
+ mSize=QSize(srcinfo.image_width, srcinfo.image_height);
+
+ jpeg_destroy_decompress(&srcinfo);
+ return true;
+ }
+};
+
+
+//------------
+//
+// JPEGContent
+//
+//------------
+JPEGContent::JPEGContent() {
+ d=new JPEGContent::Private();
+}
+
+
+JPEGContent::~JPEGContent() {
+ delete d;
+}
+
+
+bool JPEGContent::load(const QString& path) {
+ QFile file(path);
+ if (!file.open(IO_ReadOnly)) {
+ kdError() << "Could not open '" << path << "' for reading\n";
+ return false;
+ }
+ return loadFromData(file.readAll());
+}
+
+
+bool JPEGContent::loadFromData(const QByteArray& data) {
+ d->mPendingTransformation = false;
+ d->mTransformMatrix.reset();
+
+ d->mRawData = data;
+ if (d->mRawData.size()==0) {
+ kdError() << "No data\n";
+ return false;
+ }
+
+ if (!d->readSize()) return false;
+
+ Exiv2::Image::AutoPtr image;
+ try {
+ image = Exiv2::ImageFactory::open((unsigned char*)data.data(), data.size());
+ image->readMetadata();
+ } catch (Exiv2::Error&) {
+ kdError() << "Could not load image with Exiv2\n";
+ return false;
+ }
+
+ d->mExifData = image->exifData();
+ d->mComment = QString::fromUtf8( image->comment().c_str() );
+
+ d->mAperture=aperture();
+ d->mExposureTime=exposureTime();
+ d->mIso=iso();
+ d->mFocalLength=iso();
+
+ // Adjust the size according to the orientation
+ switch (orientation()) {
+ case TRANSPOSE:
+ case ROT_90:
+ case TRANSVERSE:
+ case ROT_270:
+ d->mSize.transpose();
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+
+Orientation JPEGContent::orientation() const {
+ Exiv2::ExifKey key("Exif.Image.Orientation");
+ Exiv2::ExifData::iterator it = d->mExifData.findKey(key);
+ if (it == d->mExifData.end()) {
+ return NOT_AVAILABLE;
+ }
+ return Orientation( it->toLong() );
+}
+
+
+int JPEGContent::dotsPerMeterX() const {
+ return dotsPerMeter("XResolution");
+}
+
+
+int JPEGContent::dotsPerMeterY() const {
+ return dotsPerMeter("YResolution");
+}
+
+
+int JPEGContent::dotsPerMeter(const QString& keyName) const {
+ Exiv2::ExifKey keyResUnit("Exif.Image.ResolutionUnit");
+ Exiv2::ExifData::iterator it = d->mExifData.findKey(keyResUnit);
+ if (it == d->mExifData.end()) {
+ return 0;
+ }
+ int res = it->toLong();
+ QString keyVal = "Exif.Image." + keyName;
+ Exiv2::ExifKey keyResolution(keyVal.ascii());
+ it = d->mExifData.findKey(keyResolution);
+ if (it == d->mExifData.end()) {
+ return 0;
+ }
+ // The unit for measuring XResolution and YResolution. The same unit is used for both XResolution and YResolution.
+ // If the image resolution in unknown, 2 (inches) is designated.
+ // Default = 2
+ // 2 = inches
+ // 3 = centimeters
+ // Other = reserved
+ const float INCHESPERMETER = (100. / 2.54);
+ Exiv2::Rational r = it->toRational();
+ if (r.second == 0) {
+ // a rational with 0 as second will make hang toLong() conversion
+ r.second = 1;
+ }
+ switch (res) {
+ case 3: // dots per cm
+ return int(float(r.first) * 100 / float(r.second));
+ default: // dots per inch
+ return int(float(r.first) * INCHESPERMETER / float(r.second));
+ }
+
+ return 0;
+}
+
+
+void JPEGContent::resetOrientation() {
+ Exiv2::ExifKey key("Exif.Image.Orientation");
+ Exiv2::ExifData::iterator it = d->mExifData.findKey(key);
+ if (it == d->mExifData.end()) {
+ return;
+ }
+
+ *it = uint16_t(ImageUtils::NORMAL);
+}
+
+
+QSize JPEGContent::size() const {
+ return d->mSize;
+}
+
+
+QString JPEGContent::comment() const {
+ return d->mComment;
+}
+
+QString JPEGContent::getExifInformation(const QString exifkey) const {
+ QString ret;
+
+ Exiv2::ExifKey key(exifkey.latin1());
+ Exiv2::ExifData::iterator it = d->mExifData.findKey(key);
+
+ if (it != d->mExifData.end()) {
+ std::ostringstream outputString;
+ outputString << *it;
+ ret=QString(outputString.str().c_str());
+ }
+ else {
+ ret="n/a";
+ }
+ return ret;
+}
+
+QString JPEGContent::aperture() const {
+ d->mAperture=getExifInformation("Exif.Photo.FNumber");
+ return d->mAperture;
+}
+
+QString JPEGContent::exposureTime() const {
+ d->mExposureTime=getExifInformation("Exif.Photo.ExposureTime");
+ return d->mExposureTime;
+}
+
+QString JPEGContent::iso() const {
+ d->mIso=getExifInformation("Exif.Photo.ISOSpeedRatings");
+ return d->mIso;
+}
+
+QString JPEGContent::focalLength() const {
+ d->mFocalLength=getExifInformation("Exif.Photo.FocalLength");
+ return d->mFocalLength;
+}
+
+void JPEGContent::setComment(const QString& comment) {
+ d->mComment = comment;
+}
+
+static QWMatrix createRotMatrix(int angle) {
+ QWMatrix matrix;
+ matrix.rotate(angle);
+ return matrix;
+}
+
+
+static QWMatrix createScaleMatrix(int dx, int dy) {
+ QWMatrix matrix;
+ matrix.scale(dx, dy);
+ return matrix;
+}
+
+
+
+struct OrientationInfo {
+ OrientationInfo() {}
+ OrientationInfo(Orientation o, QWMatrix m, JXFORM_CODE j)
+ : orientation(o), matrix(m), jxform(j) {}
+
+ Orientation orientation;
+ QWMatrix matrix;
+ JXFORM_CODE jxform;
+};
+typedef QValueList<OrientationInfo> OrientationInfoList;
+
+static const OrientationInfoList& orientationInfoList() {
+ static OrientationInfoList list;
+ if (list.size() == 0) {
+ QWMatrix rot90 = createRotMatrix(90);
+ QWMatrix hflip = createScaleMatrix(-1, 1);
+ QWMatrix vflip = createScaleMatrix(1, -1);
+
+ list
+ << OrientationInfo(NOT_AVAILABLE, QWMatrix(), JXFORM_NONE)
+ << OrientationInfo(NORMAL, QWMatrix(), JXFORM_NONE)
+ << OrientationInfo(HFLIP, hflip, JXFORM_FLIP_H)
+ << OrientationInfo(ROT_180, createRotMatrix(180), JXFORM_ROT_180)
+ << OrientationInfo(VFLIP, vflip, JXFORM_FLIP_V)
+ << OrientationInfo(TRANSPOSE, hflip * rot90, JXFORM_TRANSPOSE)
+ << OrientationInfo(ROT_90, rot90, JXFORM_ROT_90)
+ << OrientationInfo(TRANSVERSE, vflip * rot90, JXFORM_TRANSVERSE)
+ << OrientationInfo(ROT_270, createRotMatrix(270), JXFORM_ROT_270)
+ ;
+ }
+ return list;
+}
+
+
+void JPEGContent::transform(Orientation orientation) {
+ if (orientation != NOT_AVAILABLE && orientation != NORMAL) {
+ d->mPendingTransformation = true;
+ OrientationInfoList::ConstIterator it(orientationInfoList().begin()), end(orientationInfoList().end());
+ for (; it!=end; ++it) {
+ if ( (*it).orientation == orientation ) {
+ d->mTransformMatrix = (*it).matrix * d->mTransformMatrix;
+ break;
+ }
+ }
+ if (it == end) {
+ kdWarning() << k_funcinfo << "Could not find matrix for orientation\n";
+ }
+ }
+}
+
+
+#if 0
+static void dumpMatrix(const QWMatrix& matrix) {
+ kdDebug() << "matrix | " << matrix.m11() << ", " << matrix.m12() << " |\n";
+ kdDebug() << " | " << matrix.m21() << ", " << matrix.m22() << " |\n";
+ kdDebug() << " ( " << matrix.dx() << ", " << matrix.dy() << " )\n";
+}
+#endif
+
+
+static bool matricesAreSame(const QWMatrix& m1, const QWMatrix& m2, double tolerance) {
+ return fabs( m1.m11() - m2.m11() ) < tolerance
+ && fabs( m1.m12() - m2.m12() ) < tolerance
+ && fabs( m1.m21() - m2.m21() ) < tolerance
+ && fabs( m1.m22() - m2.m22() ) < tolerance
+ && fabs( m1.dx() - m2.dx() ) < tolerance
+ && fabs( m1.dy() - m2.dy() ) < tolerance;
+}
+
+
+static JXFORM_CODE findJxform(const QWMatrix& matrix) {
+ OrientationInfoList::ConstIterator it(orientationInfoList().begin()), end(orientationInfoList().end());
+ for (; it!=end; ++it) {
+ if ( matricesAreSame( (*it).matrix, matrix, 0.001) ) {
+ return (*it).jxform;
+ }
+ }
+ kdWarning() << "findJxform: failed\n";
+ return JXFORM_NONE;
+}
+
+
+void JPEGContent::applyPendingTransformation() {
+ if (d->mRawData.size()==0) {
+ kdError() << "No data loaded\n";
+ return;
+ }
+
+ // The following code is inspired by jpegtran.c from the libjpeg
+
+ // Init JPEG structs
+ struct jpeg_decompress_struct srcinfo;
+ struct jpeg_compress_struct dstinfo;
+ jvirt_barray_ptr * src_coef_arrays;
+ jvirt_barray_ptr * dst_coef_arrays;
+
+ // Initialize the JPEG decompression object
+ JPEGErrorManager srcErrorManager;
+ srcinfo.err = &srcErrorManager;
+ jpeg_create_decompress(&srcinfo);
+ if (setjmp(srcErrorManager.jmp_buffer)) {
+ kdError() << k_funcinfo << "libjpeg error in src\n";
+ return;
+ }
+
+ // Initialize the JPEG compression object
+ JPEGErrorManager dstErrorManager;
+ dstinfo.err = &dstErrorManager;
+ jpeg_create_compress(&dstinfo);
+ if (setjmp(dstErrorManager.jmp_buffer)) {
+ kdError() << k_funcinfo << "libjpeg error in dst\n";
+ return;
+ }
+
+ // Specify data source for decompression
+ d->setupInmemSource(&srcinfo);
+
+ // Enable saving of extra markers that we want to copy
+ jcopy_markers_setup(&srcinfo, JCOPYOPT_ALL);
+
+ (void) jpeg_read_header(&srcinfo, TRUE);
+
+ // Init transformation
+ jpeg_transform_info transformoption;
+ transformoption.transform = findJxform(d->mTransformMatrix);
+ transformoption.force_grayscale = false;
+ transformoption.trim = false;
+ jtransform_request_workspace(&srcinfo, &transformoption);
+
+ /* Read source file as DCT coefficients */
+ src_coef_arrays = jpeg_read_coefficients(&srcinfo);
+
+ /* Initialize destination compression parameters from source values */
+ jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
+
+ /* Adjust destination parameters if required by transform options;
+ * also find out which set of coefficient arrays will hold the output.
+ */
+ dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo,
+ src_coef_arrays,
+ &transformoption);
+
+ /* Specify data destination for compression */
+ QByteArray output;
+ output.resize(d->mRawData.size());
+ d->setupInmemDestination(&dstinfo, &output);
+
+ /* Start compressor (note no image data is actually written here) */
+ jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
+
+ /* Copy to the output file any extra markers that we want to preserve */
+ jcopy_markers_execute(&srcinfo, &dstinfo, JCOPYOPT_ALL);
+
+ /* Execute image transformation, if any */
+ jtransform_execute_transformation(&srcinfo, &dstinfo,
+ src_coef_arrays,
+ &transformoption);
+
+ /* Finish compression and release memory */
+ jpeg_finish_compress(&dstinfo);
+ jpeg_destroy_compress(&dstinfo);
+ (void) jpeg_finish_decompress(&srcinfo);
+ jpeg_destroy_decompress(&srcinfo);
+
+ // Set rawData to our new JPEG
+ d->mRawData = output;
+}
+
+
+QImage JPEGContent::thumbnail() const {
+ QImage image;
+ if (!d->mExifData.empty()) {
+ Exiv2::DataBuf thumbnail = d->mExifData.copyThumbnail();
+ image.loadFromData(thumbnail.pData_, thumbnail.size_);
+ }
+ return image;
+}
+
+
+void JPEGContent::setThumbnail(const QImage& thumbnail) {
+ if (d->mExifData.empty()) {
+ return;
+ }
+
+ QByteArray array;
+ QBuffer buffer(array);
+ buffer.open(IO_WriteOnly);
+ QImageIO iio(&buffer, "JPEG");
+ iio.setImage(thumbnail);
+ if (!iio.write()) {
+ kdError() << "Could not write thumbnail\n";
+ return;
+ }
+
+ d->mExifData.setJpegThumbnail((unsigned char*)array.data(), array.size());
+}
+
+
+bool JPEGContent::save(const QString& path) {
+ QFile file(path);
+ if (!file.open(IO_WriteOnly)) {
+ kdError() << "Could not open '" << path << "' for writing\n";
+ return false;
+ }
+
+ return save(&file);
+}
+
+
+bool JPEGContent::save(QFile* file) {
+ if (d->mRawData.size()==0) {
+ kdError() << "No data to store in '" << file->name() << "'\n";
+ return false;
+ }
+
+ if (d->mPendingTransformation) {
+ applyPendingTransformation();
+ d->mPendingTransformation = false;
+ }
+
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((unsigned char*)d->mRawData.data(), d->mRawData.size());
+
+ // Store Exif info
+ image->setExifData(d->mExifData);
+ image->setComment(d->mComment.utf8().data());
+ image->writeMetadata();
+
+ // Update mRawData
+ Exiv2::BasicIo& io = image->io();
+ d->mRawData.resize(io.size());
+ io.read((unsigned char*)d->mRawData.data(), io.size());
+
+ QDataStream stream(file);
+ stream.writeRawBytes(d->mRawData.data(), d->mRawData.size());
+
+ // Make sure we are up to date
+ loadFromData(d->mRawData);
+ return true;
+}
+
+
+} // namespace
diff --git a/src/imageutils/jpegcontent.h b/src/imageutils/jpegcontent.h
new file mode 100644
index 0000000..40af44d
--- /dev/null
+++ b/src/imageutils/jpegcontent.h
@@ -0,0 +1,86 @@
+// vim: set tabstop=4 shiftwidth=4 noexpandtab
+/*
+Gwenview - A simple image viewer for KDE
+Copyright 2000-2004 Aurélien Gâteau
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#ifndef JPEGCONTENT_H
+#define JPEGCONTENT_H
+
+// Qt
+#include <qcstring.h>
+
+// Local
+#include <imageutils/orientation.h>
+
+#include "../gvcore/libgwenview_export.h"
+
+class QImage;
+class QString;
+class QFile;
+
+namespace ImageUtils {
+
+
+class LIBGWENVIEW_EXPORT JPEGContent {
+public:
+ JPEGContent();
+ ~JPEGContent();
+
+ Orientation orientation() const;
+ void resetOrientation();
+
+ int dotsPerMeterX() const;
+ int dotsPerMeterY() const;
+
+ QSize size() const;
+
+ QString comment() const;
+ void setComment(const QString&);
+
+ QString aperture() const;
+ QString exposureTime() const;
+ QString iso() const;
+ QString focalLength() const;
+
+ QString getExifInformation(const QString exifkey) const;
+
+ void transform(Orientation);
+
+ QImage thumbnail() const;
+ void setThumbnail(const QImage&);
+
+ bool load(const QString& file);
+ bool loadFromData(const QByteArray& rawData);
+ bool save(const QString& file);
+ bool save(QFile*);
+
+private:
+ struct Private;
+ Private *d;
+
+ JPEGContent(const JPEGContent&);
+ void operator=(const JPEGContent&);
+ void applyPendingTransformation();
+ int dotsPerMeter(const QString& keyName) const;
+};
+
+
+} // namespace
+
+
+#endif /* JPEGCONTENT_H */
diff --git a/src/imageutils/jpegerrormanager.h b/src/imageutils/jpegerrormanager.h
new file mode 100644
index 0000000..e4f82bb
--- /dev/null
+++ b/src/imageutils/jpegerrormanager.h
@@ -0,0 +1,61 @@
+// vim: set tabstop=4 shiftwidth=4 noexpandtab
+/*
+Gwenview - A simple image viewer for KDE
+Copyright 2000-2004 Aurélien Gâteau
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#ifndef JPEGERRORMANAGER_H
+#define JPEGERRORMANAGER_H
+
+#include <setjmp.h>
+
+extern "C" {
+#define XMD_H
+#include <jpeglib.h>
+#undef const
+}
+
+namespace ImageUtils {
+
+/**
+ * A simple error manager which overrides jpeg_error_mgr.error_exit to avoid
+ * calls to exit(). It uses setjmp, which I don't like, but I don't fill like
+ * introducing exceptions to the code base for now.
+ *
+ * In order to use it, give an instance of it to jpeg_decompress_struct.err,
+ * then call setjmp(errorManager.jmp_buffer)
+ */
+struct JPEGErrorManager : public jpeg_error_mgr {
+ JPEGErrorManager() : jpeg_error_mgr() {
+ jpeg_std_error(this);
+ error_exit=errorExitCallBack;
+ }
+
+ jmp_buf jmp_buffer;
+
+ static void errorExitCallBack (j_common_ptr cinfo) {
+ JPEGErrorManager* myerr = static_cast<JPEGErrorManager*>(cinfo->err);
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message)(cinfo, buffer);
+ kdWarning() << k_funcinfo << buffer << endl;
+ longjmp(myerr->jmp_buffer, 1);
+ }
+};
+
+} // namespace
+
+#endif /* JPEGERRORMANAGER_H */
diff --git a/src/imageutils/jpegint.h b/src/imageutils/jpegint.h
new file mode 100644
index 0000000..95b00d4
--- /dev/null
+++ b/src/imageutils/jpegint.h
@@ -0,0 +1,392 @@
+/*
+ * jpegint.h
+ *
+ * Copyright (C) 1991-1997, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file provides common declarations for the various JPEG modules.
+ * These declarations are considered internal to the JPEG library; most
+ * applications using the library shouldn't need to include this file.
+ */
+
+
+/* Declarations for both compression & decompression */
+
+typedef enum { /* Operating modes for buffer controllers */
+ JBUF_PASS_THRU, /* Plain stripwise operation */
+ /* Remaining modes require a full-image buffer to have been created */
+ JBUF_SAVE_SOURCE, /* Run source subobject only, save output */
+ JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */
+ JBUF_SAVE_AND_PASS /* Run both subobjects, save output */
+} J_BUF_MODE;
+
+/* Values of global_state field (jdapi.c has some dependencies on ordering!) */
+#define CSTATE_START 100 /* after create_compress */
+#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */
+#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */
+#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */
+#define DSTATE_START 200 /* after create_decompress */
+#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */
+#define DSTATE_READY 202 /* found SOS, ready for start_decompress */
+#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/
+#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */
+#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */
+#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */
+#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */
+#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */
+#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */
+#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */
+
+
+/* Declarations for compression modules */
+
+/* Master control module */
+struct jpeg_comp_master {
+ JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo));
+ JMETHOD(void, pass_startup, (j_compress_ptr cinfo));
+ JMETHOD(void, finish_pass, (j_compress_ptr cinfo));
+
+ /* State variables made visible to other modules */
+ boolean call_pass_startup; /* True if pass_startup must be called */
+ boolean is_last_pass; /* True during last pass */
+};
+
+/* Main buffer control (downsampled-data buffer) */
+struct jpeg_c_main_controller {
+ JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode));
+ JMETHOD(void, process_data, (j_compress_ptr cinfo,
+ JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
+ JDIMENSION in_rows_avail));
+};
+
+/* Compression preprocessing (downsampling input buffer control) */
+struct jpeg_c_prep_controller {
+ JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode));
+ JMETHOD(void, pre_process_data, (j_compress_ptr cinfo,
+ JSAMPARRAY input_buf,
+ JDIMENSION *in_row_ctr,
+ JDIMENSION in_rows_avail,
+ JSAMPIMAGE output_buf,
+ JDIMENSION *out_row_group_ctr,
+ JDIMENSION out_row_groups_avail));
+};
+
+/* Coefficient buffer control */
+struct jpeg_c_coef_controller {
+ JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode));
+ JMETHOD(boolean, compress_data, (j_compress_ptr cinfo,
+ JSAMPIMAGE input_buf));
+};
+
+/* Colorspace conversion */
+struct jpeg_color_converter {
+ JMETHOD(void, start_pass, (j_compress_ptr cinfo));
+ JMETHOD(void, color_convert, (j_compress_ptr cinfo,
+ JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+ JDIMENSION output_row, int num_rows));
+};
+
+/* Downsampling */
+struct jpeg_downsampler {
+ JMETHOD(void, start_pass, (j_compress_ptr cinfo));
+ JMETHOD(void, downsample, (j_compress_ptr cinfo,
+ JSAMPIMAGE input_buf, JDIMENSION in_row_index,
+ JSAMPIMAGE output_buf,
+ JDIMENSION out_row_group_index));
+
+ boolean need_context_rows; /* TRUE if need rows above & below */
+};
+
+/* Forward DCT (also controls coefficient quantization) */
+struct jpeg_forward_dct {
+ JMETHOD(void, start_pass, (j_compress_ptr cinfo));
+ /* perhaps this should be an array??? */
+ JMETHOD(void, forward_DCT, (j_compress_ptr cinfo,
+ jpeg_component_info * compptr,
+ JSAMPARRAY sample_data, JBLOCKROW coef_blocks,
+ JDIMENSION start_row, JDIMENSION start_col,
+ JDIMENSION num_blocks));
+};
+
+/* Entropy encoding */
+struct jpeg_entropy_encoder {
+ JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics));
+ JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data));
+ JMETHOD(void, finish_pass, (j_compress_ptr cinfo));
+};
+
+/* Marker writing */
+struct jpeg_marker_writer {
+ JMETHOD(void, write_file_header, (j_compress_ptr cinfo));
+ JMETHOD(void, write_frame_header, (j_compress_ptr cinfo));
+ JMETHOD(void, write_scan_header, (j_compress_ptr cinfo));
+ JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo));
+ JMETHOD(void, write_tables_only, (j_compress_ptr cinfo));
+ /* These routines are exported to allow insertion of extra markers */
+ /* Probably only COM and APPn markers should be written this way */
+ JMETHOD(void, write_marker_header, (j_compress_ptr cinfo, int marker,
+ unsigned int datalen));
+ JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val));
+};
+
+
+/* Declarations for decompression modules */
+
+/* Master control module */
+struct jpeg_decomp_master {
+ JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo));
+ JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo));
+
+ /* State variables made visible to other modules */
+ boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */
+};
+
+/* Input control module */
+struct jpeg_input_controller {
+ JMETHOD(int, consume_input, (j_decompress_ptr cinfo));
+ JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo));
+ JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo));
+ JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo));
+
+ /* State variables made visible to other modules */
+ boolean has_multiple_scans; /* True if file has multiple scans */
+ boolean eoi_reached; /* True when EOI has been consumed */
+};
+
+/* Main buffer control (downsampled-data buffer) */
+struct jpeg_d_main_controller {
+ JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode));
+ JMETHOD(void, process_data, (j_decompress_ptr cinfo,
+ JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+ JDIMENSION out_rows_avail));
+};
+
+/* Coefficient buffer control */
+struct jpeg_d_coef_controller {
+ JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo));
+ JMETHOD(int, consume_data, (j_decompress_ptr cinfo));
+ JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo));
+ JMETHOD(int, decompress_data, (j_decompress_ptr cinfo,
+ JSAMPIMAGE output_buf));
+ /* Pointer to array of coefficient virtual arrays, or NULL if none */
+ jvirt_barray_ptr *coef_arrays;
+};
+
+/* Decompression postprocessing (color quantization buffer control) */
+struct jpeg_d_post_controller {
+ JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode));
+ JMETHOD(void, post_process_data, (j_decompress_ptr cinfo,
+ JSAMPIMAGE input_buf,
+ JDIMENSION *in_row_group_ctr,
+ JDIMENSION in_row_groups_avail,
+ JSAMPARRAY output_buf,
+ JDIMENSION *out_row_ctr,
+ JDIMENSION out_rows_avail));
+};
+
+/* Marker reading & parsing */
+struct jpeg_marker_reader {
+ JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo));
+ /* Read markers until SOS or EOI.
+ * Returns same codes as are defined for jpeg_consume_input:
+ * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI.
+ */
+ JMETHOD(int, read_markers, (j_decompress_ptr cinfo));
+ /* Read a restart marker --- exported for use by entropy decoder only */
+ jpeg_marker_parser_method read_restart_marker;
+
+ /* State of marker reader --- nominally internal, but applications
+ * supplying COM or APPn handlers might like to know the state.
+ */
+ boolean saw_SOI; /* found SOI? */
+ boolean saw_SOF; /* found SOF? */
+ int next_restart_num; /* next restart number expected (0-7) */
+ unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */
+};
+
+/* Entropy decoding */
+struct jpeg_entropy_decoder {
+ JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+ JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo,
+ JBLOCKROW *MCU_data));
+
+ /* This is here to share code between baseline and progressive decoders; */
+ /* other modules probably should not use it */
+ boolean insufficient_data; /* set TRUE after emitting warning */
+};
+
+/* Inverse DCT (also performs dequantization) */
+typedef JMETHOD(void, inverse_DCT_method_ptr,
+ (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+ JCOEFPTR coef_block,
+ JSAMPARRAY output_buf, JDIMENSION output_col));
+
+struct jpeg_inverse_dct {
+ JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+ /* It is useful to allow each component to have a separate IDCT method. */
+ inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS];
+};
+
+/* Upsampling (note that upsampler must also call color converter) */
+struct jpeg_upsampler {
+ JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+ JMETHOD(void, upsample, (j_decompress_ptr cinfo,
+ JSAMPIMAGE input_buf,
+ JDIMENSION *in_row_group_ctr,
+ JDIMENSION in_row_groups_avail,
+ JSAMPARRAY output_buf,
+ JDIMENSION *out_row_ctr,
+ JDIMENSION out_rows_avail));
+
+ boolean need_context_rows; /* TRUE if need rows above & below */
+};
+
+/* Colorspace conversion */
+struct jpeg_color_deconverter {
+ JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+ JMETHOD(void, color_convert, (j_decompress_ptr cinfo,
+ JSAMPIMAGE input_buf, JDIMENSION input_row,
+ JSAMPARRAY output_buf, int num_rows));
+};
+
+/* Color quantization or color precision reduction */
+struct jpeg_color_quantizer {
+ JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan));
+ JMETHOD(void, color_quantize, (j_decompress_ptr cinfo,
+ JSAMPARRAY input_buf, JSAMPARRAY output_buf,
+ int num_rows));
+ JMETHOD(void, finish_pass, (j_decompress_ptr cinfo));
+ JMETHOD(void, new_color_map, (j_decompress_ptr cinfo));
+};
+
+
+/* Miscellaneous useful macros */
+
+#undef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#undef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+
+/* We assume that right shift corresponds to signed division by 2 with
+ * rounding towards minus infinity. This is correct for typical "arithmetic
+ * shift" instructions that shift in copies of the sign bit. But some
+ * C compilers implement >> with an unsigned shift. For these machines you
+ * must define RIGHT_SHIFT_IS_UNSIGNED.
+ * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity.
+ * It is only applied with constant shift counts. SHIFT_TEMPS must be
+ * included in the variables of any routine using RIGHT_SHIFT.
+ */
+
+#ifdef RIGHT_SHIFT_IS_UNSIGNED
+#define SHIFT_TEMPS INT32 shift_temp;
+#define RIGHT_SHIFT(x,shft) \
+ ((shift_temp = (x)) < 0 ? \
+ (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \
+ (shift_temp >> (shft)))
+#else
+#define SHIFT_TEMPS
+#define RIGHT_SHIFT(x,shft) ((x) >> (shft))
+#endif
+
+
+/* Short forms of external names for systems with brain-damaged linkers. */
+
+#ifdef NEED_SHORT_EXTERNAL_NAMES
+#define jinit_compress_master jICompress
+#define jinit_c_master_control jICMaster
+#define jinit_c_main_controller jICMainC
+#define jinit_c_prep_controller jICPrepC
+#define jinit_c_coef_controller jICCoefC
+#define jinit_color_converter jICColor
+#define jinit_downsampler jIDownsampler
+#define jinit_forward_dct jIFDCT
+#define jinit_huff_encoder jIHEncoder
+#define jinit_phuff_encoder jIPHEncoder
+#define jinit_marker_writer jIMWriter
+#define jinit_master_decompress jIDMaster
+#define jinit_d_main_controller jIDMainC
+#define jinit_d_coef_controller jIDCoefC
+#define jinit_d_post_controller jIDPostC
+#define jinit_input_controller jIInCtlr
+#define jinit_marker_reader jIMReader
+#define jinit_huff_decoder jIHDecoder
+#define jinit_phuff_decoder jIPHDecoder
+#define jinit_inverse_dct jIIDCT
+#define jinit_upsampler jIUpsampler
+#define jinit_color_deconverter jIDColor
+#define jinit_1pass_quantizer jI1Quant
+#define jinit_2pass_quantizer jI2Quant
+#define jinit_merged_upsampler jIMUpsampler
+#define jinit_memory_mgr jIMemMgr
+#define jdiv_round_up jDivRound
+#define jround_up jRound
+#define jcopy_sample_rows jCopySamples
+#define jcopy_block_row jCopyBlocks
+#define jzero_far jZeroFar
+#define jpeg_zigzag_order jZIGTable
+#define jpeg_natural_order jZAGTable
+#endif /* NEED_SHORT_EXTERNAL_NAMES */
+
+
+/* Compression module initialization routines */
+EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo,
+ boolean transcode_only));
+EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo,
+ boolean need_full_buffer));
+EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo,
+ boolean need_full_buffer));
+EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo,
+ boolean need_full_buffer));
+EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo));
+/* Decompression module initialization routines */
+EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo,
+ boolean need_full_buffer));
+EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo,
+ boolean need_full_buffer));
+EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo,
+ boolean need_full_buffer));
+EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo));
+/* Memory manager initialization */
+EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo));
+
+/* Utility routines in jutils.c */
+EXTERN(long) jdiv_round_up JPP((long a, long b));
+EXTERN(long) jround_up JPP((long a, long b));
+EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row,
+ JSAMPARRAY output_array, int dest_row,
+ int num_rows, JDIMENSION num_cols));
+EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row,
+ JDIMENSION num_blocks));
+EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero));
+/* Constant tables in jutils.c */
+#if 0 /* This table is not actually needed in v6a */
+extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */
+#endif
+extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */
+
+/* Suppress undefined-structure complaints if necessary. */
+
+#ifdef INCOMPLETE_TYPES_BROKEN
+#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */
+struct jvirt_sarray_control { long dummy; };
+struct jvirt_barray_control { long dummy; };
+#endif
+#endif /* INCOMPLETE_TYPES_BROKEN */
diff --git a/src/imageutils/orient6.jpg b/src/imageutils/orient6.jpg
new file mode 100644
index 0000000..db1002b
--- /dev/null
+++ b/src/imageutils/orient6.jpg
Binary files differ
diff --git a/src/imageutils/orientation.h b/src/imageutils/orientation.h
new file mode 100644
index 0000000..64f0ad8
--- /dev/null
+++ b/src/imageutils/orientation.h
@@ -0,0 +1,58 @@
+// vim: set tabstop=4 shiftwidth=4 noexpandtab
+/*
+Gwenview - A simple image viewer for KDE
+Copyright 2000-2004 Aurélien Gâteau
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#ifndef ORIENTATION_H
+#define ORIENTATION_H
+
+
+namespace ImageUtils {
+
+/* Explanation extracted from http://sylvana.net/jpegcrop/exif_orientation.html
+
+ For convenience, here is what the letter F would look like if it were tagged
+correctly and displayed by a program that ignores the orientation tag (thus
+showing the stored image):
+
+ 1 2 3 4 5 6 7 8
+
+888888 888888 88 88 8888888888 88 88 8888888888
+88 88 88 88 88 88 88 88 88 88 88 88
+8888 8888 8888 8888 88 8888888888 8888888888 88
+88 88 88 88
+88 88 888888 888888
+
+*/
+
+enum Orientation {
+ NOT_AVAILABLE=0,
+ NORMAL=1,
+ HFLIP=2,
+ ROT_180=3,
+ VFLIP=4,
+ TRANSPOSE=5,
+ ROT_90=6,
+ TRANSVERSE=7,
+ ROT_270=8
+};
+
+}
+
+
+#endif
diff --git a/src/imageutils/scale.cpp b/src/imageutils/scale.cpp
new file mode 100644
index 0000000..99ed186
--- /dev/null
+++ b/src/imageutils/scale.cpp
@@ -0,0 +1,1979 @@
+// This file includes code for scaling images, in two versions.
+// One ported from ImageMagick (slower, but can achieve better quality),
+// and from Imlib2 ported by Mosfet (very fast).
+
+
+// ImageMagick code begin
+// ----------------------
+
+// This code is ImageMagick's resize code, adapted for QImage, with
+// fastfloat class added as an optimization.
+// The original license text follows.
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% RRRR EEEEE SSSSS IIIII ZZZZZ EEEEE %
+% R R E SS I ZZ E %
+% RRRR EEE SSS I ZZZ EEE %
+% R R E SS I ZZ E %
+% R R EEEEE SSSSS IIIII ZZZZZ EEEEE %
+% %
+% ImageMagick Image Resize Methods %
+% %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright (C) 2003 ImageMagick Studio, a non-profit organization dedicated %
+% to making software imaging solutions freely available. %
+% %
+% Permission is hereby granted, free of charge, to any person obtaining a %
+% copy of this software and associated documentation files ("ImageMagick"), %
+% to deal in ImageMagick without restriction, including without limitation %
+% the rights to use, copy, modify, merge, publish, distribute, sublicense, %
+% and/or sell copies of ImageMagick, and to permit persons to whom the %
+% ImageMagick is furnished to do so, subject to the following conditions: %
+% %
+% The above copyright notice and this permission notice shall be included in %
+% all copies or substantial portions of ImageMagick. %
+% %
+% The software is provided "as is", without warranty of any kind, express or %
+% implied, including but not limited to the warranties of merchantability, %
+% fitness for a particular purpose and noninfringement. In no event shall %
+% ImageMagick Studio be liable for any claim, damages or other liability, %
+% whether in an action of contract, tort or otherwise, arising from, out of %
+% or in connection with ImageMagick or the use or other dealings in %
+% ImageMagick. %
+% %
+% Except as contained in this notice, the name of the ImageMagick Studio %
+% shall not be used in advertising or otherwise to promote the sale, use or %
+% other dealings in ImageMagick without prior written authorization from the %
+% ImageMagick Studio. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+#include "config.h"
+
+// System
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#else
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#endif
+#endif
+
+// Qt
+#include <qimage.h>
+#include <qcolor.h>
+
+#include <kdeversion.h>
+#include <kcpuinfo.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+// Local
+#include "imageutils.h"
+
+// everything in namespace
+namespace ImageUtils {
+
+
+#define Max QMAX
+#define Min QMIN
+
+// mustn't be less than used precision (i.e. 1/fastfloat::RATIO)
+#define MagickEpsilon 0.0002
+
+// fastfloat begin
+// this class stores floating point numbers as integers, with BITS shift,
+// i.e. value XYZ is stored as XYZ * RATIO
+struct fastfloat
+ {
+ private:
+ enum { BITS = 12, RATIO = 4096 };
+ public:
+ fastfloat() {}
+ fastfloat( long v ) : value( v << BITS ) {}
+ fastfloat( int v ) : value( v << BITS ) {}
+ fastfloat( double v ) : value( static_cast< long >( v * RATIO + 0.5 )) {}
+ double toDouble() const { return static_cast< double >( value ) / RATIO; }
+ long toLong() const { return value >> BITS; }
+ fastfloat& operator += ( fastfloat r ) { value += r.value; return *this; }
+ fastfloat& operator -= ( fastfloat r ) { value -= r.value; return *this; }
+ fastfloat& operator *= ( fastfloat r ) { value = static_cast< long long >( value ) * r.value >> BITS; return *this; }
+ fastfloat& operator /= ( fastfloat r ) { value = ( static_cast< long long >( value ) << BITS ) / r.value; return *this; }
+ bool operator< ( fastfloat r ) const { return value < r.value; }
+ bool operator<= ( fastfloat r ) const { return value <= r.value; }
+ bool operator> ( fastfloat r ) const { return value > r.value; }
+ bool operator>= ( fastfloat r ) const { return value >= r.value; }
+ bool operator== ( fastfloat r ) const { return value == r.value; }
+ bool operator!= ( fastfloat r ) const { return value != r.value; }
+ fastfloat operator-() const { return fastfloat( -value, false ); }
+ private:
+ fastfloat( long v, bool ) : value( v ) {} // for operator-()
+ long value;
+ };
+
+inline fastfloat operator+ ( fastfloat l, fastfloat r ) { return fastfloat( l ) += r; }
+inline fastfloat operator- ( fastfloat l, fastfloat r ) { return fastfloat( l ) -= r; }
+inline fastfloat operator* ( fastfloat l, fastfloat r ) { return fastfloat( l ) *= r; }
+inline fastfloat operator/ ( fastfloat l, fastfloat r ) { return fastfloat( l ) /= r; }
+
+inline bool operator< ( fastfloat l, double r ) { return l < fastfloat( r ); }
+inline bool operator<= ( fastfloat l, double r ) { return l <= fastfloat( r ); }
+inline bool operator> ( fastfloat l, double r ) { return l > fastfloat( r ); }
+inline bool operator>= ( fastfloat l, double r ) { return l >= fastfloat( r ); }
+inline bool operator== ( fastfloat l, double r ) { return l == fastfloat( r ); }
+inline bool operator!= ( fastfloat l, double r ) { return l != fastfloat( r ); }
+
+inline bool operator< ( double l, fastfloat r ) { return fastfloat( l ) < r ; }
+inline bool operator<= ( double l, fastfloat r ) { return fastfloat( l ) <= r ; }
+inline bool operator> ( double l, fastfloat r ) { return fastfloat( l ) > r ; }
+inline bool operator>= ( double l, fastfloat r ) { return fastfloat( l ) >= r ; }
+inline bool operator== ( double l, fastfloat r ) { return fastfloat( l ) == r ; }
+inline bool operator!= ( double l, fastfloat r ) { return fastfloat( l ) != r ; }
+
+inline double fasttodouble( fastfloat v ) { return v.toDouble(); }
+inline long fasttolong( fastfloat v ) { return v.toLong(); }
+
+#if 1 // change to 0 to turn fastfloat usage off
+#else
+#define fastfloat double
+#define fasttodouble( v ) double( v )
+#define fasttolong( v ) long( v )
+#endif
+
+//fastfloat end
+
+
+typedef fastfloat (*Filter)(const fastfloat, const fastfloat);
+
+typedef struct _ContributionInfo
+{
+ fastfloat
+ weight;
+
+ long
+ pixel;
+} ContributionInfo;
+
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s i z e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResizeImage() scales an image to the desired dimensions with one of these
+% filters:
+%
+% Bessel Blackman Box
+% Catrom Cubic Gaussian
+% Hanning Hermite Lanczos
+% Mitchell Point Quandratic
+% Sinc Triangle
+%
+% Most of the filters are FIR (finite impulse response), however, Bessel,
+% Gaussian, and Sinc are IIR (infinite impulse response). Bessel and Sinc
+% are windowed (brought down to zero) with the Blackman filter.
+%
+% ResizeImage() was inspired by Paul Heckbert's zoom program.
+%
+% The format of the ResizeImage method is:
+%
+% Image *ResizeImage(Image *image,const unsigned long columns,
+% const unsigned long rows,const FilterTypes filter,const double blur,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: The image.
+%
+% o columns: The number of columns in the scaled image.
+%
+% o rows: The number of rows in the scaled image.
+%
+% o filter: Image filter to use.
+%
+% o blur: The blur factor where > 1 is blurry, < 1 is sharp.
+%
+% o exception: Return any errors or warnings in this structure.
+%
+%
+*/
+
+#if 0
+static fastfloat Bessel(const fastfloat x,const fastfloat)
+{
+ if (x == 0.0)
+ return(MagickPI/4.0);
+ return(BesselOrderOne(MagickPI*x)/(2.0*x));
+}
+
+static fastfloat Sinc(const fastfloat x,const fastfloat)
+{
+ if (x == 0.0)
+ return(1.0);
+ return(sin(MagickPI*x)/(MagickPI*x));
+}
+
+static fastfloat Blackman(const fastfloat x,const fastfloat)
+{
+ return(0.42+0.5*cos(MagickPI*x)+0.08*cos(2*MagickPI*x));
+}
+
+static fastfloat BlackmanBessel(const fastfloat x,const fastfloat)
+{
+ return(Blackman(x/support,support)*Bessel(x,support));
+}
+
+static fastfloat BlackmanSinc(const fastfloat x,const fastfloat)
+{
+ return(Blackman(x/support,support)*Sinc(x,support));
+}
+#endif
+
+static fastfloat Box(const fastfloat x,const fastfloat)
+{
+ if (x < -0.5)
+ return(0.0);
+ if (x < 0.5)
+ return(1.0);
+ return(0.0);
+}
+
+#if 0
+static fastfloat Catrom(const fastfloat x,const fastfloat)
+{
+ if (x < -2.0)
+ return(0.0);
+ if (x < -1.0)
+ return(0.5*(4.0+x*(8.0+x*(5.0+x))));
+ if (x < 0.0)
+ return(0.5*(2.0+x*x*(-5.0-3.0*x)));
+ if (x < 1.0)
+ return(0.5*(2.0+x*x*(-5.0+3.0*x)));
+ if (x < 2.0)
+ return(0.5*(4.0+x*(-8.0+x*(5.0-x))));
+ return(0.0);
+}
+
+static fastfloat Cubic(const fastfloat x,const fastfloat)
+{
+ if (x < -2.0)
+ return(0.0);
+ if (x < -1.0)
+ return((2.0+x)*(2.0+x)*(2.0+x)/6.0);
+ if (x < 0.0)
+ return((4.0+x*x*(-6.0-3.0*x))/6.0);
+ if (x < 1.0)
+ return((4.0+x*x*(-6.0+3.0*x))/6.0);
+ if (x < 2.0)
+ return((2.0-x)*(2.0-x)*(2.0-x)/6.0);
+ return(0.0);
+}
+
+static fastfloat Gaussian(const fastfloat x,const fastfloat)
+{
+ return(exp(-2.0*x*x)*sqrt(2.0/MagickPI));
+}
+
+static fastfloat Hanning(const fastfloat x,const fastfloat)
+{
+ return(0.5+0.5*cos(MagickPI*x));
+}
+
+static fastfloat Hamming(const fastfloat x,const fastfloat)
+{
+ return(0.54+0.46*cos(MagickPI*x));
+}
+
+static fastfloat Hermite(const fastfloat x,const fastfloat)
+{
+ if (x < -1.0)
+ return(0.0);
+ if (x < 0.0)
+ return((2.0*(-x)-3.0)*(-x)*(-x)+1.0);
+ if (x < 1.0)
+ return((2.0*x-3.0)*x*x+1.0);
+ return(0.0);
+}
+
+static fastfloat Lanczos(const fastfloat x,const fastfloat support)
+{
+ if (x < -3.0)
+ return(0.0);
+ if (x < 0.0)
+ return(Sinc(-x,support)*Sinc(-x/3.0,support));
+ if (x < 3.0)
+ return(Sinc(x,support)*Sinc(x/3.0,support));
+ return(0.0);
+}
+
+static fastfloat Mitchell(const fastfloat x,const fastfloat)
+{
+#define B (1.0/3.0)
+#define C (1.0/3.0)
+#define P0 (( 6.0- 2.0*B )/6.0)
+#define P2 ((-18.0+12.0*B+ 6.0*C)/6.0)
+#define P3 (( 12.0- 9.0*B- 6.0*C)/6.0)
+#define Q0 (( 8.0*B+24.0*C)/6.0)
+#define Q1 (( -12.0*B-48.0*C)/6.0)
+#define Q2 (( 6.0*B+30.0*C)/6.0)
+#define Q3 (( - 1.0*B- 6.0*C)/6.0)
+
+ if (x < -2.0)
+ return(0.0);
+ if (x < -1.0)
+ return(Q0-x*(Q1-x*(Q2-x*Q3)));
+ if (x < 0.0)
+ return(P0+x*x*(P2-x*P3));
+ if (x < 1.0)
+ return(P0+x*x*(P2+x*P3));
+ if (x < 2.0)
+ return(Q0+x*(Q1+x*(Q2+x*Q3)));
+ return(0.0);
+
+#undef B
+#undef C
+#undef P0
+#undef P2
+#undef P3
+#undef Q0
+#undef Q1
+#undef Q2
+#undef Q3
+}
+#endif
+
+// this is the same like Mitchell, but it has different values
+// for B and C, resulting in sharper images
+// http://sourceforge.net/mailarchive/forum.php?thread_id=7445822&forum_id=1210
+static fastfloat Bicubic(const fastfloat x,const fastfloat)
+{
+#define B (0.0/3.0)
+#define C (2.0/3.0)
+#define P0 (( 6.0- 2.0*B )/6.0)
+#define P2 ((-18.0+12.0*B+ 6.0*C)/6.0)
+#define P3 (( 12.0- 9.0*B- 6.0*C)/6.0)
+#define Q0 (( 8.0*B+24.0*C)/6.0)
+#define Q1 (( -12.0*B-48.0*C)/6.0)
+#define Q2 (( 6.0*B+30.0*C)/6.0)
+#define Q3 (( - 1.0*B- 6.0*C)/6.0)
+
+ if (x < -2.0)
+ return(0.0);
+ if (x < -1.0)
+ return(Q0-x*(Q1-x*(Q2-x*Q3)));
+ if (x < 0.0)
+ return(P0+x*x*(P2-x*P3));
+ if (x < 1.0)
+ return(P0+x*x*(P2+x*P3));
+ if (x < 2.0)
+ return(Q0+x*(Q1+x*(Q2+x*Q3)));
+ return(0.0);
+
+#undef B
+#undef C
+#undef P0
+#undef P2
+#undef P3
+#undef Q0
+#undef Q1
+#undef Q2
+#undef Q3
+}
+
+#if 0
+static fastfloat Quadratic(const fastfloat x,const fastfloat)
+{
+ if (x < -1.5)
+ return(0.0);
+ if (x < -0.5)
+ return(0.5*(x+1.5)*(x+1.5));
+ if (x < 0.5)
+ return(0.75-x*x);
+ if (x < 1.5)
+ return(0.5*(x-1.5)*(x-1.5));
+ return(0.0);
+}
+#endif
+
+static fastfloat Triangle(const fastfloat x,const fastfloat)
+{
+ if (x < -1.0)
+ return(0.0);
+ if (x < 0.0)
+ return(1.0+x);
+ if (x < 1.0)
+ return(1.0-x);
+ return(0.0);
+}
+
+static void HorizontalFilter(const QImage& source,QImage& destination,
+ const fastfloat x_factor,const fastfloat blur,
+ ContributionInfo *contribution, Filter filter, fastfloat filtersupport)
+{
+ fastfloat
+ center,
+ density,
+ scale,
+ support;
+
+ long
+ n,
+ start,
+ stop,
+ y;
+
+ register long
+ i,
+ x;
+
+ /*
+ Apply filter to resize horizontally from source to destination.
+ */
+ scale=blur*Max(1.0/x_factor,1.0);
+ support=scale* filtersupport;
+ if (support <= 0.5)
+ {
+ /*
+ Reduce to point sampling.
+ */
+ support=0.5+MagickEpsilon;
+ scale=1.0;
+ }
+ scale=1.0/scale;
+ for (x=0; x < (long) destination.width(); x++)
+ {
+ center=(fastfloat) (x+0.5)/x_factor;
+ start= fasttolong(Max(center-support+0.5,0));
+ stop= fasttolong(Min(center+support+0.5,source.width()));
+ density=0.0;
+ for (n=0; n < (stop-start); n++)
+ {
+ contribution[n].pixel=start+n;
+ contribution[n].weight=
+ filter (scale*(start+n-center+0.5), filtersupport );
+ density+=contribution[n].weight;
+ }
+ if ((density != 0.0) && (density != 1.0))
+ {
+ /*
+ Normalize.
+ */
+ density=1.0/density;
+ for (i=0; i < n; i++)
+ contribution[i].weight*=density;
+ }
+// p=AcquireImagePixels(source,contribution[0].pixel,0,contribution[n-1].pixel-
+// contribution[0].pixel+1,source->rows,exception);
+// q=SetImagePixels(destination,x,0,1,destination->rows);
+ for (y=0; y < (long) destination.height(); y++)
+ {
+ fastfloat red = 0;
+ fastfloat green = 0;
+ fastfloat blue = 0;
+ fastfloat alpha = 0;
+ for (i=0; i < n; i++)
+ {
+ int px = contribution[i].pixel;
+ int py = y;
+ QRgb p = reinterpret_cast< QRgb* >( source.jumpTable()[ py ])[ px ];
+ red+=contribution[i].weight*qRed(p);
+ green+=contribution[i].weight*qGreen(p);
+ blue+=contribution[i].weight*qBlue(p);
+ alpha+=contribution[i].weight*qAlpha(p);
+ }
+ QRgb pix = qRgba(
+ fasttolong( red < 0 ? 0 : red > 255 ? 255 : red + 0.5 ),
+ fasttolong( green < 0 ? 0 : green > 255 ? 255 : green + 0.5 ),
+ fasttolong( blue < 0 ? 0 : blue > 255 ? 255 : blue + 0.5 ),
+ fasttolong( alpha < 0 ? 0 : alpha > 255 ? 255 : alpha + 0.5 ));
+ reinterpret_cast< QRgb* >( destination.jumpTable()[ y ])[ x ] = pix;
+ }
+ }
+}
+
+static void VerticalFilter(const QImage& source,QImage& destination,
+ const fastfloat y_factor,const fastfloat blur,
+ ContributionInfo *contribution, Filter filter, fastfloat filtersupport )
+{
+ fastfloat
+ center,
+ density,
+ scale,
+ support;
+
+ long
+ n,
+ start,
+ stop,
+ x;
+
+ register long
+ i,
+ y;
+
+ /*
+ Apply filter to resize vertically from source to destination.
+ */
+ scale=blur*Max(1.0/y_factor,1.0);
+ support=scale* filtersupport;
+ if (support <= 0.5)
+ {
+ /*
+ Reduce to point sampling.
+ */
+ support=0.5+MagickEpsilon;
+ scale=1.0;
+ }
+ scale=1.0/scale;
+ for (y=0; y < (long) destination.height(); y++)
+ {
+ center=(fastfloat) (y+0.5)/y_factor;
+ start= fasttolong(Max(center-support+0.5,0));
+ stop= fasttolong(Min(center+support+0.5,source.height()));
+ density=0.0;
+ for (n=0; n < (stop-start); n++)
+ {
+ contribution[n].pixel=start+n;
+ contribution[n].weight=
+ filter (scale*(start+n-center+0.5), filtersupport);
+ density+=contribution[n].weight;
+ }
+ if ((density != 0.0) && (density != 1.0))
+ {
+ /*
+ Normalize.
+ */
+ density=1.0/density;
+ for (i=0; i < n; i++)
+ contribution[i].weight*=density;
+ }
+// p=AcquireImagePixels(source,0,contribution[0].pixel,source->columns,
+// contribution[n-1].pixel-contribution[0].pixel+1,exception);
+// q=SetImagePixels(destination,0,y,destination->columns,1);
+ for (x=0; x < (long) destination.width(); x++)
+ {
+ fastfloat red = 0;
+ fastfloat green = 0;
+ fastfloat blue = 0;
+ fastfloat alpha = 0;
+ for (i=0; i < n; i++)
+ {
+ int px = x;
+ int py = contribution[i].pixel;
+ QRgb p = reinterpret_cast< QRgb* >( source.jumpTable()[ py ])[ px ];
+ red+=contribution[i].weight*qRed(p);
+ green+=contribution[i].weight*qGreen(p);
+ blue+=contribution[i].weight*qBlue(p);
+ alpha+=contribution[i].weight*qAlpha(p);
+ }
+ QRgb pix = qRgba(
+ fasttolong( red < 0 ? 0 : red > 255 ? 255 : red + 0.5 ),
+ fasttolong( green < 0 ? 0 : green > 255 ? 255 : green + 0.5 ),
+ fasttolong( blue < 0 ? 0 : blue > 255 ? 255 : blue + 0.5 ),
+ fasttolong( alpha < 0 ? 0 : alpha > 255 ? 255 : alpha + 0.5 ));
+ reinterpret_cast< QRgb* >( destination.jumpTable()[ y ])[ x ] = pix;
+ }
+ }
+}
+
+static QImage ResizeImage(const QImage& image,const int columns,
+ const int rows, Filter filter, fastfloat filtersupport, double blur)
+{
+ ContributionInfo
+ *contribution;
+
+ fastfloat
+ support,
+ x_factor,
+ x_support,
+ y_factor,
+ y_support;
+
+ /*
+ Initialize resize image attributes.
+ */
+ if ((columns == image.width()) && (rows == image.height()) && (blur == 1.0))
+ return image.copy();
+ QImage resize_image( columns, rows, 32 );
+ resize_image.setAlphaBuffer( image.hasAlphaBuffer());
+ /*
+ Allocate filter contribution info.
+ */
+ x_factor=(fastfloat) resize_image.width()/image.width();
+ y_factor=(fastfloat) resize_image.height()/image.height();
+// i=(long) LanczosFilter;
+// if (image->filter != UndefinedFilter)
+// i=(long) image->filter;
+// else
+// if ((image->storage_class == PseudoClass) || image->matte ||
+// ((x_factor*y_factor) > 1.0))
+// i=(long) MitchellFilter;
+ x_support=blur*Max(1.0/x_factor,1.0)*filtersupport;
+ y_support=blur*Max(1.0/y_factor,1.0)*filtersupport;
+ support=Max(x_support,y_support);
+ if (support < filtersupport)
+ support=filtersupport;
+ contribution=new ContributionInfo[ fasttolong( 2.0*Max(support,0.5)+3 ) ];
+ Q_CHECK_PTR( contribution );
+ /*
+ Resize image.
+ */
+ if (((fastfloat) columns*(image.height()+rows)) >
+ ((fastfloat) rows*(image.width()+columns)))
+ {
+ QImage source_image( columns, image.height(), 32 );
+ source_image.setAlphaBuffer( image.hasAlphaBuffer());
+ HorizontalFilter(image,source_image,x_factor,blur,
+ contribution,filter,filtersupport);
+ VerticalFilter(source_image,resize_image,y_factor,
+ blur,contribution,filter,filtersupport);
+ }
+ else
+ {
+ QImage source_image( image.width(), rows, 32 );
+ source_image.setAlphaBuffer( image.hasAlphaBuffer());
+ VerticalFilter(image,source_image,y_factor,blur,
+ contribution,filter,filtersupport);
+ HorizontalFilter(source_image,resize_image,x_factor,
+ blur,contribution,filter,filtersupport);
+ }
+ /*
+ Free allocated memory.
+ */
+ delete[] contribution;
+ return(resize_image);
+}
+
+
+#undef Max
+#undef Min
+#undef MagickEpsilon
+
+
+// filters and their matching support values
+#if 0
+ static const FilterInfo
+ filters[SincFilter+1] =
+ {
+ { Box, 0.0 },
+ { Box, 0.0 },
+ { Box, 0.5 },
+ { Triangle, 1.0 },
+ { Hermite, 1.0 },
+ { Hanning, 1.0 },
+ { Hamming, 1.0 },
+ { Blackman, 1.0 },
+ { Gaussian, 1.25 },
+ { Quadratic, 1.5 },
+ { Cubic, 2.0 },
+ { Catrom, 2.0 },
+ { Mitchell, 2.0 },
+ { Lanczos, 3.0 },
+ { BlackmanBessel, 3.2383 },
+ { BlackmanSinc, 4.0 }
+ };
+#endif
+
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S a m p l e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SampleImage() scales an image to the desired dimensions with pixel
+% sampling. Unlike other scaling methods, this method does not introduce
+% any additional color into the scaled image.
+%
+% The format of the SampleImage method is:
+%
+% Image *SampleImage(const Image *image,const unsigned long columns,
+% const unsigned long rows,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: The image.
+%
+% o columns: The number of columns in the sampled image.
+%
+% o rows: The number of rows in the sampled image.
+%
+% o exception: Return any errors or warnings in this structure.
+%
+%
+*/
+QImage SampleImage(const QImage& image,const int columns,
+ const int rows)
+{
+ int
+ *x_offset,
+ *y_offset;
+
+ long
+ j,
+ y;
+
+ uchar
+ *pixels;
+
+ register const uchar
+ *p;
+
+ register long
+ x;
+
+ register uchar
+ *q;
+
+ /*
+ Initialize sampled image attributes.
+ */
+ if ((columns == image.width()) && (rows == image.height()))
+ return image;
+ // This function is modified to handle any image depth, not only
+ // 32bit like the ImageMagick original. This avoids the relatively
+ // expensive conversion.
+ const int d = image.depth() / 8;
+ QImage sample_image( columns, rows, image.depth());
+ sample_image.setAlphaBuffer( image.hasAlphaBuffer());
+ /*
+ Allocate scan line buffer and column offset buffers.
+ */
+ pixels= new uchar[ image.width() * d ];
+ x_offset= new int[ sample_image.width() ];
+ y_offset= new int[ sample_image.height() ];
+ /*
+ Initialize pixel offsets.
+ */
+// In the following several code 0.5 needs to be added, otherwise the image
+// would be moved by half a pixel to bottom-right, just like
+// with Qt's QImage::scale()
+ for (x=0; x < (long) sample_image.width(); x++)
+ {
+ x_offset[x]=int((x+0.5)*image.width()/sample_image.width());
+ }
+ for (y=0; y < (long) sample_image.height(); y++)
+ {
+ y_offset[y]=int((y+0.5)*image.height()/sample_image.height());
+ }
+ /*
+ Sample each row.
+ */
+ j=(-1);
+ for (y=0; y < (long) sample_image.height(); y++)
+ {
+ q= sample_image.scanLine( y );
+ if (j != y_offset[y] )
+ {
+ /*
+ Read a scan line.
+ */
+ j= y_offset[y];
+ p= image.scanLine( j );
+ (void) memcpy(pixels,p,image.width()*d);
+ }
+ /*
+ Sample each column.
+ */
+ switch( d )
+ {
+ case 1: // 8bit
+ for (x=0; x < (long) sample_image.width(); x++)
+ {
+ *q++=pixels[ x_offset[x] ];
+ }
+ break;
+ case 4: // 32bit
+ for (x=0; x < (long) sample_image.width(); x++)
+ {
+ *(QRgb*)q=((QRgb*)pixels)[ x_offset[x] ];
+ q += d;
+ }
+ break;
+ default:
+ for (x=0; x < (long) sample_image.width(); x++)
+ {
+ memcpy( q, pixels + x_offset[x] * d, d );
+ q += d;
+ }
+ break;
+ }
+ }
+ if( d != 4 ) // != 32bit
+ {
+ sample_image.setNumColors( image.numColors());
+ for( int i = 0; i < image.numColors(); ++i )
+ sample_image.setColor( i, image.color( i ));
+ }
+ delete[] y_offset;
+ delete[] x_offset;
+ delete[] pixels;
+ return sample_image;
+}
+
+
+// ImageMagick code end
+
+
+// Imlib2/Mosfet code begin
+// ------------------------
+
+// This code is Imlib2 code, additionally modified by Mosfet, and with few small
+// modifications for Gwenview. The MMX scaling code also belongs to it.
+
+// The original license texts follow.
+
+/**
+ * This is the normal smoothscale method, based on Imlib2's smoothscale.
+ *
+ * Originally I took the algorithm used in NetPBM and Qt and added MMX/3dnow
+ * optimizations. It ran in about 1/2 the time as Qt. Then I ported Imlib's
+ * C algorithm and it ran at about the same speed as my MMX optimized one...
+ * Finally I ported Imlib's MMX version and it ran in less than half the
+ * time as my MMX algorithm, (taking only a quarter of the time Qt does).
+ *
+ * Changes include formatting, namespaces and other C++'ings, removal of old
+ * #ifdef'ed code, and removal of unneeded border calculation code.
+ *
+ * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code
+ * is by Willem Monsuwe <willem@stack.nl>. All other modifications are
+ * (C) Daniel M. Duley.
+ */
+
+/*
+ Copyright (C) 2004 Daniel M. Duley <dan.duley@verizon.net>
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+/*
+Copyright (C) 2000 Carsten Haitzler and various contributors (see AUTHORS)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies of the Software and its Copyright notices. In addition publicly
+documented acknowledgment must be given that this software has been used if no
+source code of this software is made available publicly. This includes
+acknowledgments in either Copyright notices, Manuals, Publicity and Marketing
+documents or any documentation provided with any product containing this
+software. This License does not apply to any software that links to the
+libraries provided by this software (statically or dynamically), but only to
+the software provided.
+
+Please see the COPYING.PLAIN for a plain-english explanation of this notice
+and it's intent.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+namespace MImageScale{
+ typedef struct __mimage_scale_info
+ {
+ int *xpoints;
+ unsigned int **ypoints;
+ int *xapoints, *yapoints;
+ int xup_yup;
+ } MImageScaleInfo;
+
+ unsigned int** mimageCalcYPoints(unsigned int *src, int sow, int sh,
+ int dh);
+ int* mimageCalcXPoints(int sw, int dw);
+ int* mimageCalcApoints(int s, int d, int up);
+ MImageScaleInfo* mimageFreeScaleInfo(MImageScaleInfo *isi);
+ MImageScaleInfo *mimageCalcScaleInfo(QImage &img, int sw, int sh,
+ int dw, int dh, char aa, int sow);
+ void mimageSampleRGBA(MImageScaleInfo *isi, unsigned int *dest, int dxx,
+ int dyy, int dx, int dy, int dw, int dh, int dow);
+ void mimageScaleAARGBA(MImageScaleInfo *isi, unsigned int *dest, int dxx,
+ int dyy, int dx, int dy, int dw, int dh, int dow,
+ int sow);
+ void mimageScaleAARGB(MImageScaleInfo *isi, unsigned int *dest, int dxx,
+ int dyy, int dx, int dy, int dw, int dh, int dow, int
+ sow);
+ QImage smoothScale(const QImage& img, int dw, int dh);
+ typedef long long llong;
+}
+
+#ifdef HAVE_X86_MMX
+extern "C" {
+ void __mimageScale_mmx_AARGBA(MImageScale::MImageScaleInfo *isi,
+ unsigned int *dest, int dxx, int dyy,
+ int dx, int dy, int dw, int dh,
+ int dow, int sow);
+}
+#endif
+
+using namespace MImageScale;
+
+QImage MImageScale::smoothScale(const QImage& image, int dw, int dh)
+{
+ QImage img = image.depth() < 32 ? image.convertDepth( 32 ) : image;
+ int w = img.width();
+ int h = img.height();
+
+ int sow = img.bytesPerLine();
+ // handle CroppedQImage
+ if( img.height() > 1 && sow != img.scanLine( 1 ) - img.scanLine( 0 ))
+ sow = img.scanLine( 1 ) - img.scanLine( 0 );
+ sow = sow / ( img.depth() / 8 );
+
+ MImageScaleInfo *scaleinfo =
+ mimageCalcScaleInfo(img, w, h, dw, dh, true, sow);
+ if(!scaleinfo)
+ return QImage();
+
+ QImage buffer(dw, dh, 32);
+ buffer.setAlphaBuffer(img.hasAlphaBuffer());
+
+#ifdef HAVE_X86_MMX
+//#warning Using MMX Smoothscale
+ bool haveMMX = KCPUInfo::haveExtension( KCPUInfo::IntelMMX );
+ if(haveMMX){
+ __mimageScale_mmx_AARGBA(scaleinfo, (unsigned int *)buffer.scanLine(0),
+ 0, 0, 0, 0, dw, dh, dw, sow);
+ }
+ else
+#endif
+ {
+ if(img.hasAlphaBuffer())
+ mimageScaleAARGBA(scaleinfo, (unsigned int *)buffer.scanLine(0), 0, 0,
+ 0, 0, dw, dh, dw, sow);
+ else
+ mimageScaleAARGB(scaleinfo, (unsigned int *)buffer.scanLine(0), 0, 0,
+ 0, 0, dw, dh, dw, sow);
+ }
+ mimageFreeScaleInfo(scaleinfo);
+ return(buffer);
+}
+
+//
+// Code ported from Imlib...
+//
+
+// FIXME: replace with mRed, etc... These work on pointers to pixels, not
+// pixel values
+#if BYTE_ORDER == BIG_ENDIAN
+#define A_VAL(p) ((unsigned char *)(p))[0]
+#define R_VAL(p) ((unsigned char *)(p))[1]
+#define G_VAL(p) ((unsigned char *)(p))[2]
+#define B_VAL(p) ((unsigned char *)(p))[3]
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#define A_VAL(p) ((unsigned char *)(p))[3]
+#define R_VAL(p) ((unsigned char *)(p))[2]
+#define G_VAL(p) ((unsigned char *)(p))[1]
+#define B_VAL(p) ((unsigned char *)(p))[0]
+#else
+#error "BYTE_ORDER is not defined"
+#endif
+
+#define INV_XAP (256 - xapoints[x])
+#define XAP (xapoints[x])
+#define INV_YAP (256 - yapoints[dyy + y])
+#define YAP (yapoints[dyy + y])
+
+unsigned int** MImageScale::mimageCalcYPoints(unsigned int *src,
+ int sow, int sh, int dh)
+{
+ unsigned int **p;
+ int i, j = 0;
+ int rv = 0;
+ llong val, inc;
+
+ if(dh < 0){
+ dh = -dh;
+ rv = 1;
+ }
+ p = new unsigned int* [dh+1];
+
+ val = 0;
+ inc = (llong(sh) << 16) / dh;
+ for(i = 0; i < dh; i++){
+ p[j++] = src + ((val >> 16) * sow);
+ val += inc;
+ }
+ if(rv){
+ for(i = dh / 2; --i >= 0; ){
+ unsigned int *tmp = p[i];
+ p[i] = p[dh - i - 1];
+ p[dh - i - 1] = tmp;
+ }
+ }
+ return(p);
+}
+
+int* MImageScale::mimageCalcXPoints(int sw, int dw)
+{
+ int *p, i, j = 0;
+ int rv = 0;
+ llong val, inc;
+
+ if(dw < 0){
+ dw = -dw;
+ rv = 1;
+ }
+ p = new int[dw+1];
+
+ val = 0;
+ inc = (llong(sw) << 16) / dw;
+ for(i = 0; i < dw; i++){
+ p[j++] = (val >> 16);
+ val += inc;
+ }
+
+ if(rv){
+ for(i = dw / 2; --i >= 0; ){
+ int tmp = p[i];
+ p[i] = p[dw - i - 1];
+ p[dw - i - 1] = tmp;
+ }
+ }
+ return(p);
+}
+
+int* MImageScale::mimageCalcApoints(int s, int d, int up)
+{
+ int *p, i, j = 0, rv = 0;
+
+ if(d < 0){
+ rv = 1;
+ d = -d;
+ }
+ p = new int[d];
+
+ /* scaling up */
+ if(up){
+ llong val, inc;
+
+ val = 0;
+ inc = (llong(s) << 16) / d;
+ for(i = 0; i < d; i++){
+ p[j++] = (val >> 8) - ((val >> 8) & 0xffffff00);
+ if((val >> 16) >= (s - 1))
+ p[j - 1] = 0;
+ val += inc;
+ }
+ }
+ /* scaling down */
+ else{
+ llong val, inc;
+ int ap, Cp;
+ val = 0;
+ inc = (llong(s) << 16) / d;
+ Cp = ((llong(d) << 14) / s) + 1;
+ for(i = 0; i < d; i++){
+ ap = ((0x100 - ((val >> 8) & 0xff)) * Cp) >> 8;
+ p[j] = ap | (Cp << 16);
+ j++;
+ val += inc;
+ }
+ }
+ if(rv){
+ int tmp;
+ for(i = d / 2; --i >= 0; ){
+ tmp = p[i];
+ p[i] = p[d - i - 1];
+ p[d - i - 1] = tmp;
+ }
+ }
+ return(p);
+}
+
+MImageScaleInfo* MImageScale::mimageFreeScaleInfo(MImageScaleInfo *isi)
+{
+ if(isi){
+ delete[] isi->xpoints;
+ delete[] isi->ypoints;
+ delete[] isi->xapoints;
+ delete[] isi->yapoints;
+ delete isi;
+ }
+ return(NULL);
+}
+
+MImageScaleInfo* MImageScale::mimageCalcScaleInfo(QImage &img, int sw, int sh,
+ int dw, int dh, char aa, int sow)
+{
+ MImageScaleInfo *isi;
+ int scw, sch;
+
+ scw = dw * img.width() / sw;
+ sch = dh * img.height() / sh;
+
+ isi = new MImageScaleInfo;
+ if(!isi)
+ return(NULL);
+ memset(isi, 0, sizeof(MImageScaleInfo));
+
+ isi->xup_yup = (abs(dw) >= sw) + ((abs(dh) >= sh) << 1);
+
+ isi->xpoints = mimageCalcXPoints(img.width(), scw);
+ if(!isi->xpoints)
+ return(mimageFreeScaleInfo(isi));
+ isi->ypoints = mimageCalcYPoints((unsigned int *)img.scanLine(0),
+ sow, img.height(), sch );
+ if (!isi->ypoints)
+ return(mimageFreeScaleInfo(isi));
+ if(aa){
+ isi->xapoints = mimageCalcApoints(img.width(), scw, isi->xup_yup & 1);
+ if(!isi->xapoints)
+ return(mimageFreeScaleInfo(isi));
+ isi->yapoints = mimageCalcApoints(img.height(), sch, isi->xup_yup & 2);
+ if(!isi->yapoints)
+ return(mimageFreeScaleInfo(isi));
+ }
+ return(isi);
+}
+
+/* scale by pixel sampling only */
+void MImageScale::mimageSampleRGBA(MImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow)
+{
+ unsigned int *sptr, *dptr;
+ int x, y, end;
+ unsigned int **ypoints = isi->ypoints;
+ int *xpoints = isi->xpoints;
+
+ /* whats the last pixel ont he line so we stop there */
+ end = dxx + dw;
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ /* get the pointer to the start of the destination scanline */
+ dptr = dest + dx + ((y + dy) * dow);
+ /* calculate the source line we'll scan from */
+ sptr = ypoints[dyy + y];
+ /* go thru the scanline and copy across */
+ for(x = dxx; x < end; x++)
+ *dptr++ = sptr[xpoints[x]];
+ }
+}
+
+/* FIXME: NEED to optimise ScaleAARGBA - currently its "ok" but needs work*/
+
+/* scale by area sampling */
+void MImageScale::mimageScaleAARGBA(MImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ unsigned int *sptr, *dptr;
+ int x, y, end;
+ unsigned int **ypoints = isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ end = dxx + dw;
+ /* scaling up both ways */
+ if(isi->xup_yup == 3){
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ /* calculate the source line we'll scan from */
+ dptr = dest + dx + ((y + dy) * dow);
+ sptr = ypoints[dyy + y];
+ if(YAP > 0){
+ for(x = dxx; x < end; x++){
+ int r, g, b, a;
+ int rr, gg, bb, aa;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ a = A_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ a += A_VAL(pix) * XAP;
+ pix += sow;
+ rr = R_VAL(pix) * XAP;
+ gg = G_VAL(pix) * XAP;
+ bb = B_VAL(pix) * XAP;
+ aa = A_VAL(pix) * XAP;
+ pix--;
+ rr += R_VAL(pix) * INV_XAP;
+ gg += G_VAL(pix) * INV_XAP;
+ bb += B_VAL(pix) * INV_XAP;
+ aa += A_VAL(pix) * INV_XAP;
+ r = ((rr * YAP) + (r * INV_YAP)) >> 16;
+ g = ((gg * YAP) + (g * INV_YAP)) >> 16;
+ b = ((bb * YAP) + (b * INV_YAP)) >> 16;
+ a = ((aa * YAP) + (a * INV_YAP)) >> 16;
+ *dptr++ = qRgba(r, g, b, a);
+ }
+ else{
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_YAP;
+ g = G_VAL(pix) * INV_YAP;
+ b = B_VAL(pix) * INV_YAP;
+ a = A_VAL(pix) * INV_YAP;
+ pix += sow;
+ r += R_VAL(pix) * YAP;
+ g += G_VAL(pix) * YAP;
+ b += B_VAL(pix) * YAP;
+ a += A_VAL(pix) * YAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ a >>= 8;
+ *dptr++ = qRgba(r, g, b, a);
+ }
+ }
+ }
+ else{
+ for(x = dxx; x < end; x++){
+ int r, g, b, a;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ a = A_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ a += A_VAL(pix) * XAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ a >>= 8;
+ *dptr++ = qRgba(r, g, b, a);
+ }
+ else
+ *dptr++ = sptr[xpoints[x] ];
+ }
+ }
+ }
+ }
+ /* if we're scaling down vertically */
+ else if(isi->xup_yup == 1){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cy, j;
+ unsigned int *pix;
+ int r, g, b, a, rr, gg, bb, aa;
+ int yap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL(pix) * yap) >> 10;
+ g = (G_VAL(pix) * yap) >> 10;
+ b = (B_VAL(pix) * yap) >> 10;
+ a = (A_VAL(pix) * yap) >> 10;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix += sow;
+ r += (R_VAL(pix) * Cy) >> 10;
+ g += (G_VAL(pix) * Cy) >> 10;
+ b += (B_VAL(pix) * Cy) >> 10;
+ a += (A_VAL(pix) * Cy) >> 10;
+ }
+ if(j > 0){
+ pix += sow;
+ r += (R_VAL(pix) * j) >> 10;
+ g += (G_VAL(pix) * j) >> 10;
+ b += (B_VAL(pix) * j) >> 10;
+ a += (A_VAL(pix) * j) >> 10;
+ }
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + 1;
+ rr = (R_VAL(pix) * yap) >> 10;
+ gg = (G_VAL(pix) * yap) >> 10;
+ bb = (B_VAL(pix) * yap) >> 10;
+ aa = (A_VAL(pix) * yap) >> 10;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix += sow;
+ rr += (R_VAL(pix) * Cy) >> 10;
+ gg += (G_VAL(pix) * Cy) >> 10;
+ bb += (B_VAL(pix) * Cy) >> 10;
+ aa += (A_VAL(pix) * Cy) >> 10;
+ }
+ if(j > 0){
+ pix += sow;
+ rr += (R_VAL(pix) * j) >> 10;
+ gg += (G_VAL(pix) * j) >> 10;
+ bb += (B_VAL(pix) * j) >> 10;
+ aa += (A_VAL(pix) * j) >> 10;
+ }
+ r = r * INV_XAP;
+ g = g * INV_XAP;
+ b = b * INV_XAP;
+ a = a * INV_XAP;
+ r = (r + ((rr * XAP))) >> 12;
+ g = (g + ((gg * XAP))) >> 12;
+ b = (b + ((bb * XAP))) >> 12;
+ a = (a + ((aa * XAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ a >>= 4;
+ }
+ *dptr = qRgba(r, g, b, a);
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally */
+ else if(isi->xup_yup == 2){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, j;
+ unsigned int *pix;
+ int r, g, b, a, rr, gg, bb, aa;
+ int xap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL(pix) * xap) >> 10;
+ g = (G_VAL(pix) * xap) >> 10;
+ b = (B_VAL(pix) * xap) >> 10;
+ a = (A_VAL(pix) * xap) >> 10;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ pix++;
+ r += (R_VAL(pix) * Cx) >> 10;
+ g += (G_VAL(pix) * Cx) >> 10;
+ b += (B_VAL(pix) * Cx) >> 10;
+ a += (A_VAL(pix) * Cx) >> 10;
+ }
+ if(j > 0){
+ pix++;
+ r += (R_VAL(pix) * j) >> 10;
+ g += (G_VAL(pix) * j) >> 10;
+ b += (B_VAL(pix) * j) >> 10;
+ a += (A_VAL(pix) * j) >> 10;
+ }
+ if(YAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + sow;
+ rr = (R_VAL(pix) * xap) >> 10;
+ gg = (G_VAL(pix) * xap) >> 10;
+ bb = (B_VAL(pix) * xap) >> 10;
+ aa = (A_VAL(pix) * xap) >> 10;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ pix++;
+ rr += (R_VAL(pix) * Cx) >> 10;
+ gg += (G_VAL(pix) * Cx) >> 10;
+ bb += (B_VAL(pix) * Cx) >> 10;
+ aa += (A_VAL(pix) * Cx) >> 10;
+ }
+ if(j > 0){
+ pix++;
+ rr += (R_VAL(pix) * j) >> 10;
+ gg += (G_VAL(pix) * j) >> 10;
+ bb += (B_VAL(pix) * j) >> 10;
+ aa += (A_VAL(pix) * j) >> 10;
+ }
+ r = r * INV_YAP;
+ g = g * INV_YAP;
+ b = b * INV_YAP;
+ a = a * INV_YAP;
+ r = (r + ((rr * YAP))) >> 12;
+ g = (g + ((gg * YAP))) >> 12;
+ b = (b + ((bb * YAP))) >> 12;
+ a = (a + ((aa * YAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ a >>= 4;
+ }
+ *dptr = qRgba(r, g, b, a);
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally & vertically */
+ else{
+ /*\ 'Correct' version, with math units prepared for MMXification:
+ |*| The operation 'b = (b * c) >> 16' translates to pmulhw,
+ |*| so the operation 'b = (b * c) >> d' would translate to
+ |*| psllw (16 - d), %mmb; pmulh %mmc, %mmb
+ \*/
+ int Cx, Cy, i, j;
+ unsigned int *pix;
+ int a, r, g, b, ax, rx, gx, bx;
+ int xap, yap;
+
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ sptr = ypoints[dyy + y] + xpoints[x];
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ ax = (A_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ ax += (A_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0){
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ ax += (A_VAL(pix) * i) >> 9;
+ }
+
+ r = (rx * yap) >> 14;
+ g = (gx * yap) >> 14;
+ b = (bx * yap) >> 14;
+ a = (ax * yap) >> 14;
+
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ ax = (A_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ ax += (A_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0){
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ ax += (A_VAL(pix) * i) >> 9;
+ }
+
+ r += (rx * Cy) >> 14;
+ g += (gx * Cy) >> 14;
+ b += (bx * Cy) >> 14;
+ a += (ax * Cy) >> 14;
+ }
+ if(j > 0){
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ ax = (A_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ ax += (A_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0){
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ ax += (A_VAL(pix) * i) >> 9;
+ }
+
+ r += (rx * j) >> 14;
+ g += (gx * j) >> 14;
+ b += (bx * j) >> 14;
+ a += (ax * j) >> 14;
+ }
+
+ R_VAL(dptr) = r >> 5;
+ G_VAL(dptr) = g >> 5;
+ B_VAL(dptr) = b >> 5;
+ A_VAL(dptr) = a >> 5;
+ dptr++;
+ }
+ }
+ }
+}
+
+/* scale by area sampling - IGNORE the ALPHA byte*/
+void MImageScale::mimageScaleAARGB(MImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ unsigned int *sptr, *dptr;
+ int x, y, end;
+ unsigned int **ypoints = isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ end = dxx + dw;
+ /* scaling up both ways */
+ if(isi->xup_yup == 3){
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ /* calculate the source line we'll scan from */
+ dptr = dest + dx + ((y + dy) * dow);
+ sptr = ypoints[dyy + y];
+ if(YAP > 0){
+ for(x = dxx; x < end; x++){
+ int r = 0, g = 0, b = 0;
+ int rr = 0, gg = 0, bb = 0;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ pix += sow;
+ rr = R_VAL(pix) * XAP;
+ gg = G_VAL(pix) * XAP;
+ bb = B_VAL(pix) * XAP;
+ pix --;
+ rr += R_VAL(pix) * INV_XAP;
+ gg += G_VAL(pix) * INV_XAP;
+ bb += B_VAL(pix) * INV_XAP;
+ r = ((rr * YAP) + (r * INV_YAP)) >> 16;
+ g = ((gg * YAP) + (g * INV_YAP)) >> 16;
+ b = ((bb * YAP) + (b * INV_YAP)) >> 16;
+ *dptr++ = qRgba(r, g, b, 0xff);
+ }
+ else{
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_YAP;
+ g = G_VAL(pix) * INV_YAP;
+ b = B_VAL(pix) * INV_YAP;
+ pix += sow;
+ r += R_VAL(pix) * YAP;
+ g += G_VAL(pix) * YAP;
+ b += B_VAL(pix) * YAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ *dptr++ = qRgba(r, g, b, 0xff);
+ }
+ }
+ }
+ else{
+ for(x = dxx; x < end; x++){
+ int r = 0, g = 0, b = 0;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ *dptr++ = qRgba(r, g, b, 0xff);
+ }
+ else
+ *dptr++ = sptr[xpoints[x] ];
+ }
+ }
+ }
+ }
+ /* if we're scaling down vertically */
+ else if(isi->xup_yup == 1){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cy, j;
+ unsigned int *pix;
+ int r, g, b, rr, gg, bb;
+ int yap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL(pix) * yap) >> 10;
+ g = (G_VAL(pix) * yap) >> 10;
+ b = (B_VAL(pix) * yap) >> 10;
+ pix += sow;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ r += (R_VAL(pix) * Cy) >> 10;
+ g += (G_VAL(pix) * Cy) >> 10;
+ b += (B_VAL(pix) * Cy) >> 10;
+ pix += sow;
+ }
+ if(j > 0){
+ r += (R_VAL(pix) * j) >> 10;
+ g += (G_VAL(pix) * j) >> 10;
+ b += (B_VAL(pix) * j) >> 10;
+ }
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + 1;
+ rr = (R_VAL(pix) * yap) >> 10;
+ gg = (G_VAL(pix) * yap) >> 10;
+ bb = (B_VAL(pix) * yap) >> 10;
+ pix += sow;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ rr += (R_VAL(pix) * Cy) >> 10;
+ gg += (G_VAL(pix) * Cy) >> 10;
+ bb += (B_VAL(pix) * Cy) >> 10;
+ pix += sow;
+ }
+ if(j > 0){
+ rr += (R_VAL(pix) * j) >> 10;
+ gg += (G_VAL(pix) * j) >> 10;
+ bb += (B_VAL(pix) * j) >> 10;
+ }
+ r = r * INV_XAP;
+ g = g * INV_XAP;
+ b = b * INV_XAP;
+ r = (r + ((rr * XAP))) >> 12;
+ g = (g + ((gg * XAP))) >> 12;
+ b = (b + ((bb * XAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ }
+ *dptr = qRgba(r, g, b, 0xff);
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally */
+ else if(isi->xup_yup == 2){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, j;
+ unsigned int *pix;
+ int r, g, b, rr, gg, bb;
+ int xap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL(pix) * xap) >> 10;
+ g = (G_VAL(pix) * xap) >> 10;
+ b = (B_VAL(pix) * xap) >> 10;
+ pix++;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ r += (R_VAL(pix) * Cx) >> 10;
+ g += (G_VAL(pix) * Cx) >> 10;
+ b += (B_VAL(pix) * Cx) >> 10;
+ pix++;
+ }
+ if(j > 0){
+ r += (R_VAL(pix) * j) >> 10;
+ g += (G_VAL(pix) * j) >> 10;
+ b += (B_VAL(pix) * j) >> 10;
+ }
+ if(YAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + sow;
+ rr = (R_VAL(pix) * xap) >> 10;
+ gg = (G_VAL(pix) * xap) >> 10;
+ bb = (B_VAL(pix) * xap) >> 10;
+ pix++;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ rr += (R_VAL(pix) * Cx) >> 10;
+ gg += (G_VAL(pix) * Cx) >> 10;
+ bb += (B_VAL(pix) * Cx) >> 10;
+ pix++;
+ }
+ if(j > 0){
+ rr += (R_VAL(pix) * j) >> 10;
+ gg += (G_VAL(pix) * j) >> 10;
+ bb += (B_VAL(pix) * j) >> 10;
+ }
+ r = r * INV_YAP;
+ g = g * INV_YAP;
+ b = b * INV_YAP;
+ r = (r + ((rr * YAP))) >> 12;
+ g = (g + ((gg * YAP))) >> 12;
+ b = (b + ((bb * YAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ }
+ *dptr = qRgba(r, g, b, 0xff);
+ dptr++;
+ }
+ }
+ }
+ /* fully optimized (i think) - onyl change of algorithm can help */
+ /* if we're scaling down horizontally & vertically */
+ else{
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, Cy, i, j;
+ unsigned int *pix;
+ int r, g, b, rx, gx, bx;
+ int xap, yap;
+
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ sptr = ypoints[dyy + y] + xpoints[x];
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0){
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ }
+
+ r = (rx * yap) >> 14;
+ g = (gx * yap) >> 14;
+ b = (bx * yap) >> 14;
+
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0){
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ }
+
+ r += (rx * Cy) >> 14;
+ g += (gx * Cy) >> 14;
+ b += (bx * Cy) >> 14;
+ }
+ if(j > 0){
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0){
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ }
+
+ r += (rx * j) >> 14;
+ g += (gx * j) >> 14;
+ b += (bx * j) >> 14;
+ }
+
+ R_VAL(dptr) = r >> 5;
+ G_VAL(dptr) = g >> 5;
+ B_VAL(dptr) = b >> 5;
+ dptr++;
+ }
+ }
+ }
+}
+
+// Imlib2/Mosfet code end
+
+
+// public functions :
+// ------------------
+
+// This function returns how many pixels around the zoomed area should be
+// included in the image. This is used when doing incremental painting, because
+// some smoothing algorithms use surrounding pixels and not including them
+// could sometimes make the edges between incremental steps visible.
+int extraScalePixels( SmoothAlgorithm alg, double zoom, double blur )
+{
+ double filtersupport = 0;
+ Filter filter = NULL;
+ switch( alg ) {
+ case SMOOTH_NONE:
+ filter = NULL;
+ filtersupport = 0.0;
+ break;
+ case SMOOTH_FAST:
+ filter = Box;
+ filtersupport = 0.5;
+ break;
+ case SMOOTH_NORMAL:
+ filter = Triangle;
+ filtersupport = 1.0;
+ break;
+ case SMOOTH_BEST:
+// filter = Mitchell;
+ filter = Bicubic;
+ filtersupport = 2.0;
+ break;
+ }
+ if( zoom == 1.0 || filtersupport == 0.0 ) return 0;
+ // Imlib2/Mosfet scale - I have really no idea how many pixels it needs
+ if( filter == Box && blur == 1.0 ) return int( 3 / zoom + 1 );
+// This is support size for ImageMagick's scaling.
+ double scale=blur*QMAX(1.0/zoom,1.0);
+ double support=scale* filtersupport;
+ if (support <= 0.5) support=0.5+0.000001;
+ return int( support + 1 );
+}
+
+QImage scale(const QImage& image, int width, int height,
+ SmoothAlgorithm alg, QImage::ScaleMode mode, double blur )
+{
+ if( image.isNull()) return image.copy();
+
+ QSize newSize( image.size() );
+ newSize.scale( QSize( width, height ), (QSize::ScaleMode)mode ); // ### remove cast in Qt 4.0
+ newSize = newSize.expandedTo( QSize( 1, 1 )); // make sure it doesn't become null
+
+ if ( newSize == image.size() ) return image.copy();
+
+ width = newSize.width();
+ height = newSize.height();
+ Filter filter = NULL;
+ fastfloat filtersupport;
+
+ switch( alg ) {
+ case SMOOTH_NONE:
+ filter = NULL;
+ filtersupport = 0.0;
+ break;
+ case SMOOTH_FAST:
+ filter = Box;
+ filtersupport = 0.5;
+ break;
+ case SMOOTH_NORMAL:
+ default:
+ filter = Triangle;
+ filtersupport = 1.0;
+ break;
+ case SMOOTH_BEST:
+// filter = Mitchell;
+ filter = Bicubic;
+ filtersupport = 2.0;
+ break;
+ }
+
+ if( filter == Box && blur == 1.0 )
+ return MImageScale::smoothScale( image, width, height );
+
+ if( filter == Box && width > image.width() && height > image.height() && blur == 1.0 ) {
+ filter = NULL; // Box doesn't really smooth when enlarging
+ }
+
+ if( filter == NULL ) {
+ return SampleImage( image, width, height ); // doesn't need 32bit
+ }
+
+ return ResizeImage( image.convertDepth( 32 ), width, height, filter, filtersupport, blur );
+ // unlike Qt's smoothScale() this function introduces new colors to grayscale images ... oh well
+}
+
+
+} // namespace
diff --git a/src/imageutils/testjpegcontent.cpp b/src/imageutils/testjpegcontent.cpp
new file mode 100644
index 0000000..b533373
--- /dev/null
+++ b/src/imageutils/testjpegcontent.cpp
@@ -0,0 +1,256 @@
+// vim: set tabstop=4 shiftwidth=4 noexpandtab
+/*
+Gwenview - A simple image viewer for KDE
+Copyright 2000-2004 Aurélien Gâteau
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#include <iostream>
+
+// Qt
+#include <qdir.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qstring.h>
+
+// KDE
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kfilemetainfo.h>
+
+// Local
+#include "imageutils/imageutils.h"
+#include "imageutils/jpegcontent.h"
+
+using namespace std;
+
+const char* ORIENT6_FILE="orient6.jpg";
+const int ORIENT6_WIDTH=128; // This size is the size *after* orientation
+const int ORIENT6_HEIGHT=256; // has been applied
+const char* CUT_FILE="cut.jpg";
+const QString ORIENT6_COMMENT="a comment";
+const QString ORIENT1_VFLIP_FILE="test_orient1_vflip.jpg";
+const char* THUMBNAIL_FILE="test_thumbnail.jpg";
+const char* TMP_FILE="tmp.jpg";
+
+
+class TestEnvironment {
+public:
+ TestEnvironment() {
+ bool result;
+ QFile in(ORIENT6_FILE);
+ result=in.open(IO_ReadOnly);
+ Q_ASSERT(result);
+
+ QFileInfo info(in);
+ int size=info.size()/2;
+
+ char* data=new char[size];
+ int readSize=in.readBlock(data, size);
+ Q_ASSERT(size==readSize);
+
+ QFile out(CUT_FILE);
+ result=out.open(IO_WriteOnly);
+ Q_ASSERT(result);
+
+ int wroteSize=out.writeBlock(data, size);
+ Q_ASSERT(size==wroteSize);
+ delete []data;
+ }
+
+ ~TestEnvironment() {
+ QDir::current().remove(CUT_FILE);
+ }
+};
+
+
+typedef QMap<QString,QString> MetaInfoMap;
+
+MetaInfoMap getMetaInfo(const QString& path) {
+ KFileMetaInfo fmi(path);
+ QStringList list=fmi.supportedKeys();
+ QStringList::ConstIterator it=list.begin();
+ MetaInfoMap map;
+
+ for ( ; it!=list.end(); ++it) {
+ KFileMetaInfoItem item=fmi.item(*it);
+ map[*it]=item.string();
+ }
+
+ return map;
+}
+
+void compareMetaInfo(const QString& path1, const QString& path2, const QStringList& ignoredKeys) {
+ MetaInfoMap mim1=getMetaInfo(path1);
+ MetaInfoMap mim2=getMetaInfo(path2);
+
+ Q_ASSERT(mim1.keys()==mim2.keys());
+ QValueList<QString> keys=mim1.keys();
+ QValueList<QString>::ConstIterator it=keys.begin();
+ for ( ; it!=keys.end(); ++it) {
+ QString key=*it;
+ if (ignoredKeys.contains(key)) continue;
+
+ if (mim1[key]!=mim2[key]) {
+ kdError() << "Meta info differs. Key:" << key << ", V1:" << mim1[key] << ", V2:" << mim2[key] << endl;
+ }
+ }
+}
+
+
+void testResetOrientation() {
+ ImageUtils::JPEGContent content;
+ bool result;
+
+ // Test resetOrientation without transform
+ result=content.load(ORIENT6_FILE);
+ Q_ASSERT(result);
+
+ content.resetOrientation();
+
+ result=content.save(TMP_FILE);
+ Q_ASSERT(result);
+
+ result=content.load(TMP_FILE);
+ Q_ASSERT(result);
+ Q_ASSERT(content.orientation() == ImageUtils::NORMAL);
+
+ // Test resetOrientation with transform
+ result=content.load(ORIENT6_FILE);
+ Q_ASSERT(result);
+
+ content.resetOrientation();
+ content.transform(ImageUtils::ROT_90);
+
+ result=content.save(TMP_FILE);
+ Q_ASSERT(result);
+
+ result=content.load(TMP_FILE);
+ Q_ASSERT(result);
+ Q_ASSERT(content.orientation() == ImageUtils::NORMAL);
+}
+
+
+/**
+ * This function tests JPEGContent::transform() by applying a ROT_90
+ * transformation, saving, reloading and applying a ROT_270 to undo the ROT_90.
+ * Saving and reloading are necessary because lossless transformation only
+ * happens in JPEGContent::save()
+ */
+void testTransform() {
+ bool result;
+ QImage finalImage, expectedImage;
+
+ ImageUtils::JPEGContent content;
+ result = content.load(ORIENT6_FILE);
+ Q_ASSERT(result);
+
+ content.transform(ImageUtils::ROT_90);
+ result = content.save(TMP_FILE);
+ Q_ASSERT(result);
+
+ result = content.load(TMP_FILE);
+ Q_ASSERT(result);
+ content.transform(ImageUtils::ROT_270);
+ result = content.save(TMP_FILE);
+ Q_ASSERT(result);
+
+ result = finalImage.load(TMP_FILE);
+ Q_ASSERT(result);
+
+ result = expectedImage.load(ORIENT6_FILE);
+ Q_ASSERT(result);
+
+ Q_ASSERT(finalImage == expectedImage);
+}
+
+
+void testSetComment() {
+ QString comment = "test comment";
+ ImageUtils::JPEGContent content;
+ bool result;
+ result = content.load(ORIENT6_FILE);
+ Q_ASSERT(result);
+
+ content.setComment(comment);
+ Q_ASSERT(content.comment() == comment);
+ result = content.save(TMP_FILE);
+ Q_ASSERT(result);
+
+ result = content.load(TMP_FILE);
+ Q_ASSERT(result);
+ Q_ASSERT(content.comment() == comment);
+}
+
+
+int main(int argc, char* argv[]) {
+ TestEnvironment testEnv;
+ bool result;
+ KAboutData aboutData("testjpegcontent", "testjpegcontent", "0");
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KApplication kapplication;
+
+ // Reading info
+ ImageUtils::JPEGContent content;
+ result=content.load(ORIENT6_FILE);
+ Q_ASSERT(result);
+ Q_ASSERT(content.orientation() == 6);
+ Q_ASSERT(content.comment() == ORIENT6_COMMENT);
+ Q_ASSERT(content.size() == QSize(ORIENT6_WIDTH, ORIENT6_HEIGHT));
+
+ // thumbnail()
+ QImage thumbnail=content.thumbnail();
+ result=thumbnail.save(THUMBNAIL_FILE, "JPEG");
+ Q_ASSERT(result);
+
+ testResetOrientation();
+ testTransform();
+ testSetComment();
+
+ // Test that rotating a file a lot of times does not cause findJxform() to fail
+ result = content.load(ORIENT6_FILE);
+ Q_ASSERT(result);
+
+ // 12*4 + 1 is the same as 1, since rotating four times brings you back
+ for(int loop=0; loop< 12*4 + 1; ++loop) {
+ content.transform(ImageUtils::ROT_90);
+ }
+ result = content.save(TMP_FILE);
+ Q_ASSERT(result);
+
+ result = content.load(TMP_FILE);
+ Q_ASSERT(result);
+
+ Q_ASSERT(content.size() == QSize(ORIENT6_HEIGHT, ORIENT6_WIDTH));
+
+
+ // Check the other meta info are still here
+ QStringList ignoredKeys;
+ ignoredKeys << "Orientation" << "Comment";
+ compareMetaInfo(ORIENT6_FILE, ORIENT1_VFLIP_FILE, ignoredKeys);
+
+ // Test that loading and manipulating a truncated file does not crash
+ result=content.load(CUT_FILE);
+ Q_ASSERT(result);
+ Q_ASSERT(content.orientation() == 6);
+ Q_ASSERT(content.comment() == ORIENT6_COMMENT);
+ content.transform(ImageUtils::VFLIP);
+ kdWarning() << "# Next function should output errors about incomplete image" << endl;
+ content.save(TMP_FILE);
+ kdWarning() << "#" << endl;
+}
diff --git a/src/imageutils/transupp.c b/src/imageutils/transupp.c
new file mode 100644
index 0000000..e5ec564
--- /dev/null
+++ b/src/imageutils/transupp.c
@@ -0,0 +1,928 @@
+/*
+ * transupp.c
+ *
+ * Copyright (C) 1997, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file contains image transformation routines and other utility code
+ * used by the jpegtran sample application. These are NOT part of the core
+ * JPEG library. But we keep these routines separate from jpegtran.c to
+ * ease the task of maintaining jpegtran-like programs that have other user
+ * interfaces.
+ */
+
+/* Although this file really shouldn't have access to the library internals,
+ * it's helpful to let it call jround_up() and jcopy_block_row().
+ */
+#define JPEG_INTERNALS
+
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "transupp.h" /* My own external interface */
+
+
+#if TRANSFORMS_SUPPORTED
+
+/*
+ * Lossless image transformation routines. These routines work on DCT
+ * coefficient arrays and thus do not require any lossy decompression
+ * or recompression of the image.
+ * Thanks to Guido Vollbeding for the initial design and code of this feature.
+ *
+ * Horizontal flipping is done in-place, using a single top-to-bottom
+ * pass through the virtual source array. It will thus be much the
+ * fastest option for images larger than main memory.
+ *
+ * The other routines require a set of destination virtual arrays, so they
+ * need twice as much memory as jpegtran normally does. The destination
+ * arrays are always written in normal scan order (top to bottom) because
+ * the virtual array manager expects this. The source arrays will be scanned
+ * in the corresponding order, which means multiple passes through the source
+ * arrays for most of the transforms. That could result in much thrashing
+ * if the image is larger than main memory.
+ *
+ * Some notes about the operating environment of the individual transform
+ * routines:
+ * 1. Both the source and destination virtual arrays are allocated from the
+ * source JPEG object, and therefore should be manipulated by calling the
+ * source's memory manager.
+ * 2. The destination's component count should be used. It may be smaller
+ * than the source's when forcing to grayscale.
+ * 3. Likewise the destination's sampling factors should be used. When
+ * forcing to grayscale the destination's sampling factors will be all 1,
+ * and we may as well take that as the effective iMCU size.
+ * 4. When "trim" is in effect, the destination's dimensions will be the
+ * trimmed values but the source's will be untrimmed.
+ * 5. All the routines assume that the source and destination buffers are
+ * padded out to a full iMCU boundary. This is true, although for the
+ * source buffer it is an undocumented property of jdcoefct.c.
+ * Notes 2,3,4 boil down to this: generally we should use the destination's
+ * dimensions and ignore the source's.
+ */
+
+
+LOCAL(void)
+do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays)
+/* Horizontal flip; done in-place, so no separate dest array is required */
+{
+ JDIMENSION MCU_cols, comp_width, blk_x, blk_y;
+ int ci, k, offset_y;
+ JBLOCKARRAY buffer;
+ JCOEFPTR ptr1, ptr2;
+ JCOEF temp1, temp2;
+ jpeg_component_info *compptr;
+
+ /* Horizontal mirroring of DCT blocks is accomplished by swapping
+ * pairs of blocks in-place. Within a DCT block, we perform horizontal
+ * mirroring by changing the signs of odd-numbered columns.
+ * Partial iMCUs at the right edge are left untouched.
+ */
+ MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_width = MCU_cols * compptr->h_samp_factor;
+ for (blk_y = 0; blk_y < compptr->height_in_blocks;
+ blk_y += compptr->v_samp_factor) {
+ buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) {
+ ptr1 = buffer[offset_y][blk_x];
+ ptr2 = buffer[offset_y][comp_width - blk_x - 1];
+ /* this unrolled loop doesn't need to know which row it's on... */
+ for (k = 0; k < DCTSIZE2; k += 2) {
+ temp1 = *ptr1; /* swap even column */
+ temp2 = *ptr2;
+ *ptr1++ = temp2;
+ *ptr2++ = temp1;
+ temp1 = *ptr1; /* swap odd column with sign change */
+ temp2 = *ptr2;
+ *ptr1++ = -temp2;
+ *ptr2++ = -temp1;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* Vertical flip */
+{
+ JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y;
+ int ci, i, j, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JBLOCKROW src_row_ptr, dst_row_ptr;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ /* We output into a separate array because we can't touch different
+ * rows of the source virtual array simultaneously. Otherwise, this
+ * is a pretty straightforward analog of horizontal flip.
+ * Within a DCT block, vertical mirroring is done by changing the signs
+ * of odd-numbered rows.
+ * Partial iMCUs at the bottom edge are copied verbatim.
+ */
+ MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_height = MCU_rows * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ if (dst_blk_y < comp_height) {
+ /* Row is within the mirrorable area. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor,
+ (JDIMENSION) compptr->v_samp_factor, FALSE);
+ } else {
+ /* Bottom-edge blocks will be copied verbatim. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, FALSE);
+ }
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ if (dst_blk_y < comp_height) {
+ /* Row is within the mirrorable area. */
+ dst_row_ptr = dst_buffer[offset_y];
+ src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1];
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks;
+ dst_blk_x++) {
+ dst_ptr = dst_row_ptr[dst_blk_x];
+ src_ptr = src_row_ptr[dst_blk_x];
+ for (i = 0; i < DCTSIZE; i += 2) {
+ /* copy even row */
+ for (j = 0; j < DCTSIZE; j++)
+ *dst_ptr++ = *src_ptr++;
+ /* copy odd row with sign change */
+ for (j = 0; j < DCTSIZE; j++)
+ *dst_ptr++ = - *src_ptr++;
+ }
+ }
+ } else {
+ /* Just copy row verbatim. */
+ jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y],
+ compptr->width_in_blocks);
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* Transpose source into destination */
+{
+ JDIMENSION dst_blk_x, dst_blk_y;
+ int ci, i, j, offset_x, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ /* Transposing pixels within a block just requires transposing the
+ * DCT coefficients.
+ * Partial iMCUs at the edges require no special treatment; we simply
+ * process all the available DCT blocks for every component.
+ */
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks;
+ dst_blk_x += compptr->h_samp_factor) {
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x,
+ (JDIMENSION) compptr->h_samp_factor, FALSE);
+ for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) {
+ src_ptr = src_buffer[offset_x][dst_blk_y + offset_y];
+ dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x];
+ for (i = 0; i < DCTSIZE; i++)
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* 90 degree rotation is equivalent to
+ * 1. Transposing the image;
+ * 2. Horizontal mirroring.
+ * These two steps are merged into a single processing routine.
+ */
+{
+ JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y;
+ int ci, i, j, offset_x, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ /* Because of the horizontal mirror step, we can't process partial iMCUs
+ * at the (output) right edge properly. They just get transposed and
+ * not mirrored.
+ */
+ MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_width = MCU_cols * compptr->h_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks;
+ dst_blk_x += compptr->h_samp_factor) {
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x,
+ (JDIMENSION) compptr->h_samp_factor, FALSE);
+ for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) {
+ src_ptr = src_buffer[offset_x][dst_blk_y + offset_y];
+ if (dst_blk_x < comp_width) {
+ /* Block is within the mirrorable area. */
+ dst_ptr = dst_buffer[offset_y]
+ [comp_width - dst_blk_x - offset_x - 1];
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ i++;
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ }
+ } else {
+ /* Edge blocks are transposed but not mirrored. */
+ dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x];
+ for (i = 0; i < DCTSIZE; i++)
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* 270 degree rotation is equivalent to
+ * 1. Horizontal mirroring;
+ * 2. Transposing the image.
+ * These two steps are merged into a single processing routine.
+ */
+{
+ JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y;
+ int ci, i, j, offset_x, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ /* Because of the horizontal mirror step, we can't process partial iMCUs
+ * at the (output) bottom edge properly. They just get transposed and
+ * not mirrored.
+ */
+ MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_height = MCU_rows * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks;
+ dst_blk_x += compptr->h_samp_factor) {
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x,
+ (JDIMENSION) compptr->h_samp_factor, FALSE);
+ for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) {
+ dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x];
+ if (dst_blk_y < comp_height) {
+ /* Block is within the mirrorable area. */
+ src_ptr = src_buffer[offset_x]
+ [comp_height - dst_blk_y - offset_y - 1];
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < DCTSIZE; j++) {
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ j++;
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ }
+ }
+ } else {
+ /* Edge blocks are transposed but not mirrored. */
+ src_ptr = src_buffer[offset_x][dst_blk_y + offset_y];
+ for (i = 0; i < DCTSIZE; i++)
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* 180 degree rotation is equivalent to
+ * 1. Vertical mirroring;
+ * 2. Horizontal mirroring.
+ * These two steps are merged into a single processing routine.
+ */
+{
+ JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y;
+ int ci, i, j, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JBLOCKROW src_row_ptr, dst_row_ptr;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE);
+ MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_width = MCU_cols * compptr->h_samp_factor;
+ comp_height = MCU_rows * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ if (dst_blk_y < comp_height) {
+ /* Row is within the vertically mirrorable area. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor,
+ (JDIMENSION) compptr->v_samp_factor, FALSE);
+ } else {
+ /* Bottom-edge rows are only mirrored horizontally. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, FALSE);
+ }
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ if (dst_blk_y < comp_height) {
+ /* Row is within the mirrorable area. */
+ dst_row_ptr = dst_buffer[offset_y];
+ src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1];
+ /* Process the blocks that can be mirrored both ways. */
+ for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) {
+ dst_ptr = dst_row_ptr[dst_blk_x];
+ src_ptr = src_row_ptr[comp_width - dst_blk_x - 1];
+ for (i = 0; i < DCTSIZE; i += 2) {
+ /* For even row, negate every odd column. */
+ for (j = 0; j < DCTSIZE; j += 2) {
+ *dst_ptr++ = *src_ptr++;
+ *dst_ptr++ = - *src_ptr++;
+ }
+ /* For odd row, negate every even column. */
+ for (j = 0; j < DCTSIZE; j += 2) {
+ *dst_ptr++ = - *src_ptr++;
+ *dst_ptr++ = *src_ptr++;
+ }
+ }
+ }
+ /* Any remaining right-edge blocks are only mirrored vertically. */
+ for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) {
+ dst_ptr = dst_row_ptr[dst_blk_x];
+ src_ptr = src_row_ptr[dst_blk_x];
+ for (i = 0; i < DCTSIZE; i += 2) {
+ for (j = 0; j < DCTSIZE; j++)
+ *dst_ptr++ = *src_ptr++;
+ for (j = 0; j < DCTSIZE; j++)
+ *dst_ptr++ = - *src_ptr++;
+ }
+ }
+ } else {
+ /* Remaining rows are just mirrored horizontally. */
+ dst_row_ptr = dst_buffer[offset_y];
+ src_row_ptr = src_buffer[offset_y];
+ /* Process the blocks that can be mirrored. */
+ for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) {
+ dst_ptr = dst_row_ptr[dst_blk_x];
+ src_ptr = src_row_ptr[comp_width - dst_blk_x - 1];
+ for (i = 0; i < DCTSIZE2; i += 2) {
+ *dst_ptr++ = *src_ptr++;
+ *dst_ptr++ = - *src_ptr++;
+ }
+ }
+ /* Any remaining right-edge blocks are only copied. */
+ for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) {
+ dst_ptr = dst_row_ptr[dst_blk_x];
+ src_ptr = src_row_ptr[dst_blk_x];
+ for (i = 0; i < DCTSIZE2; i++)
+ *dst_ptr++ = *src_ptr++;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* Transverse transpose is equivalent to
+ * 1. 180 degree rotation;
+ * 2. Transposition;
+ * or
+ * 1. Horizontal mirroring;
+ * 2. Transposition;
+ * 3. Horizontal mirroring.
+ * These steps are merged into a single processing routine.
+ */
+{
+ JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y;
+ int ci, i, j, offset_x, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE);
+ MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_width = MCU_cols * compptr->h_samp_factor;
+ comp_height = MCU_rows * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks;
+ dst_blk_x += compptr->h_samp_factor) {
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x,
+ (JDIMENSION) compptr->h_samp_factor, FALSE);
+ for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) {
+ if (dst_blk_y < comp_height) {
+ src_ptr = src_buffer[offset_x]
+ [comp_height - dst_blk_y - offset_y - 1];
+ if (dst_blk_x < comp_width) {
+ /* Block is within the mirrorable area. */
+ dst_ptr = dst_buffer[offset_y]
+ [comp_width - dst_blk_x - offset_x - 1];
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < DCTSIZE; j++) {
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ j++;
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ }
+ i++;
+ for (j = 0; j < DCTSIZE; j++) {
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ j++;
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ }
+ }
+ } else {
+ /* Right-edge blocks are mirrored in y only */
+ dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x];
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < DCTSIZE; j++) {
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ j++;
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ }
+ }
+ }
+ } else {
+ src_ptr = src_buffer[offset_x][dst_blk_y + offset_y];
+ if (dst_blk_x < comp_width) {
+ /* Bottom-edge blocks are mirrored in x only */
+ dst_ptr = dst_buffer[offset_y]
+ [comp_width - dst_blk_x - offset_x - 1];
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ i++;
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ }
+ } else {
+ /* At lower right corner, just transpose, no mirroring */
+ dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x];
+ for (i = 0; i < DCTSIZE; i++)
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/* Request any required workspace.
+ *
+ * We allocate the workspace virtual arrays from the source decompression
+ * object, so that all the arrays (both the original data and the workspace)
+ * will be taken into account while making memory management decisions.
+ * Hence, this routine must be called after jpeg_read_header (which reads
+ * the image dimensions) and before jpeg_read_coefficients (which realizes
+ * the source's virtual arrays).
+ */
+
+GLOBAL(void)
+jtransform_request_workspace (j_decompress_ptr srcinfo,
+ jpeg_transform_info *info)
+{
+ jvirt_barray_ptr *coef_arrays = NULL;
+ jpeg_component_info *compptr;
+ int ci;
+
+ if (info->force_grayscale &&
+ srcinfo->jpeg_color_space == JCS_YCbCr &&
+ srcinfo->num_components == 3) {
+ /* We'll only process the first component */
+ info->num_components = 1;
+ } else {
+ /* Process all the components */
+ info->num_components = srcinfo->num_components;
+ }
+
+ switch (info->transform) {
+ case JXFORM_NONE:
+ case JXFORM_FLIP_H:
+ /* Don't need a workspace array */
+ break;
+ case JXFORM_FLIP_V:
+ case JXFORM_ROT_180:
+ /* Need workspace arrays having same dimensions as source image.
+ * Note that we allocate arrays padded out to the next iMCU boundary,
+ * so that transform routines need not worry about missing edge blocks.
+ */
+ coef_arrays = (jvirt_barray_ptr *)
+ (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE,
+ SIZEOF(jvirt_barray_ptr) * info->num_components);
+ for (ci = 0; ci < info->num_components; ci++) {
+ compptr = srcinfo->comp_info + ci;
+ coef_arrays[ci] = (*srcinfo->mem->request_virt_barray)
+ ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE,
+ (JDIMENSION) jround_up((long) compptr->width_in_blocks,
+ (long) compptr->h_samp_factor),
+ (JDIMENSION) jround_up((long) compptr->height_in_blocks,
+ (long) compptr->v_samp_factor),
+ (JDIMENSION) compptr->v_samp_factor);
+ }
+ break;
+ case JXFORM_TRANSPOSE:
+ case JXFORM_TRANSVERSE:
+ case JXFORM_ROT_90:
+ case JXFORM_ROT_270:
+ /* Need workspace arrays having transposed dimensions.
+ * Note that we allocate arrays padded out to the next iMCU boundary,
+ * so that transform routines need not worry about missing edge blocks.
+ */
+ coef_arrays = (jvirt_barray_ptr *)
+ (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE,
+ SIZEOF(jvirt_barray_ptr) * info->num_components);
+ for (ci = 0; ci < info->num_components; ci++) {
+ compptr = srcinfo->comp_info + ci;
+ coef_arrays[ci] = (*srcinfo->mem->request_virt_barray)
+ ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE,
+ (JDIMENSION) jround_up((long) compptr->height_in_blocks,
+ (long) compptr->v_samp_factor),
+ (JDIMENSION) jround_up((long) compptr->width_in_blocks,
+ (long) compptr->h_samp_factor),
+ (JDIMENSION) compptr->h_samp_factor);
+ }
+ break;
+ }
+ info->workspace_coef_arrays = coef_arrays;
+}
+
+
+/* Transpose destination image parameters */
+
+LOCAL(void)
+transpose_critical_parameters (j_compress_ptr dstinfo)
+{
+ int tblno, i, j, ci, itemp;
+ jpeg_component_info *compptr;
+ JQUANT_TBL *qtblptr;
+ JDIMENSION dtemp;
+ UINT16 qtemp;
+
+ /* Transpose basic image dimensions */
+ dtemp = dstinfo->image_width;
+ dstinfo->image_width = dstinfo->image_height;
+ dstinfo->image_height = dtemp;
+
+ /* Transpose sampling factors */
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ itemp = compptr->h_samp_factor;
+ compptr->h_samp_factor = compptr->v_samp_factor;
+ compptr->v_samp_factor = itemp;
+ }
+
+ /* Transpose quantization tables */
+ for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) {
+ qtblptr = dstinfo->quant_tbl_ptrs[tblno];
+ if (qtblptr != NULL) {
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < i; j++) {
+ qtemp = qtblptr->quantval[i*DCTSIZE+j];
+ qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i];
+ qtblptr->quantval[j*DCTSIZE+i] = qtemp;
+ }
+ }
+ }
+ }
+}
+
+
+/* Trim off any partial iMCUs on the indicated destination edge */
+
+LOCAL(void)
+trim_right_edge (j_compress_ptr dstinfo)
+{
+ int ci, max_h_samp_factor;
+ JDIMENSION MCU_cols;
+
+ /* We have to compute max_h_samp_factor ourselves,
+ * because it hasn't been set yet in the destination
+ * (and we don't want to use the source's value).
+ */
+ max_h_samp_factor = 1;
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ int h_samp_factor = dstinfo->comp_info[ci].h_samp_factor;
+ max_h_samp_factor = MAX(max_h_samp_factor, h_samp_factor);
+ }
+ MCU_cols = dstinfo->image_width / (max_h_samp_factor * DCTSIZE);
+ if (MCU_cols > 0) /* can't trim to 0 pixels */
+ dstinfo->image_width = MCU_cols * (max_h_samp_factor * DCTSIZE);
+}
+
+LOCAL(void)
+trim_bottom_edge (j_compress_ptr dstinfo)
+{
+ int ci, max_v_samp_factor;
+ JDIMENSION MCU_rows;
+
+ /* We have to compute max_v_samp_factor ourselves,
+ * because it hasn't been set yet in the destination
+ * (and we don't want to use the source's value).
+ */
+ max_v_samp_factor = 1;
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ int v_samp_factor = dstinfo->comp_info[ci].v_samp_factor;
+ max_v_samp_factor = MAX(max_v_samp_factor, v_samp_factor);
+ }
+ MCU_rows = dstinfo->image_height / (max_v_samp_factor * DCTSIZE);
+ if (MCU_rows > 0) /* can't trim to 0 pixels */
+ dstinfo->image_height = MCU_rows * (max_v_samp_factor * DCTSIZE);
+}
+
+
+/* Adjust output image parameters as needed.
+ *
+ * This must be called after jpeg_copy_critical_parameters()
+ * and before jpeg_write_coefficients().
+ *
+ * The return value is the set of virtual coefficient arrays to be written
+ * (either the ones allocated by jtransform_request_workspace, or the
+ * original source data arrays). The caller will need to pass this value
+ * to jpeg_write_coefficients().
+ */
+
+GLOBAL(jvirt_barray_ptr *)
+jtransform_adjust_parameters (j_decompress_ptr srcinfo,
+ j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jpeg_transform_info *info)
+{
+ /* If force-to-grayscale is requested, adjust destination parameters */
+ if (info->force_grayscale) {
+ /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed
+ * properly. Among other things, the target h_samp_factor & v_samp_factor
+ * will get set to 1, which typically won't match the source.
+ * In fact we do this even if the source is already grayscale; that
+ * provides an easy way of coercing a grayscale JPEG with funny sampling
+ * factors to the customary 1,1. (Some decoders fail on other factors.)
+ */
+ if ((dstinfo->jpeg_color_space == JCS_YCbCr &&
+ dstinfo->num_components == 3) ||
+ (dstinfo->jpeg_color_space == JCS_GRAYSCALE &&
+ dstinfo->num_components == 1)) {
+ /* We have to preserve the source's quantization table number. */
+ int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no;
+ jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE);
+ dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no;
+ } else {
+ /* Sorry, can't do it */
+ ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL);
+ }
+ }
+
+ /* Correct the destination's image dimensions etc if necessary */
+ switch (info->transform) {
+ case JXFORM_NONE:
+ /* Nothing to do */
+ break;
+ case JXFORM_FLIP_H:
+ if (info->trim)
+ trim_right_edge(dstinfo);
+ break;
+ case JXFORM_FLIP_V:
+ if (info->trim)
+ trim_bottom_edge(dstinfo);
+ break;
+ case JXFORM_TRANSPOSE:
+ transpose_critical_parameters(dstinfo);
+ /* transpose does NOT have to trim anything */
+ break;
+ case JXFORM_TRANSVERSE:
+ transpose_critical_parameters(dstinfo);
+ if (info->trim) {
+ trim_right_edge(dstinfo);
+ trim_bottom_edge(dstinfo);
+ }
+ break;
+ case JXFORM_ROT_90:
+ transpose_critical_parameters(dstinfo);
+ if (info->trim)
+ trim_right_edge(dstinfo);
+ break;
+ case JXFORM_ROT_180:
+ if (info->trim) {
+ trim_right_edge(dstinfo);
+ trim_bottom_edge(dstinfo);
+ }
+ break;
+ case JXFORM_ROT_270:
+ transpose_critical_parameters(dstinfo);
+ if (info->trim)
+ trim_bottom_edge(dstinfo);
+ break;
+ }
+
+ /* Return the appropriate output data set */
+ if (info->workspace_coef_arrays != NULL)
+ return info->workspace_coef_arrays;
+ return src_coef_arrays;
+}
+
+
+/* Execute the actual transformation, if any.
+ *
+ * This must be called *after* jpeg_write_coefficients, because it depends
+ * on jpeg_write_coefficients to have computed subsidiary values such as
+ * the per-component width and height fields in the destination object.
+ *
+ * Note that some transformations will modify the source data arrays!
+ */
+
+GLOBAL(void)
+jtransform_execute_transformation (j_decompress_ptr srcinfo,
+ j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jpeg_transform_info *info)
+{
+ jvirt_barray_ptr *dst_coef_arrays = info->workspace_coef_arrays;
+
+ switch (info->transform) {
+ case JXFORM_NONE:
+ break;
+ case JXFORM_FLIP_H:
+ do_flip_h(srcinfo, dstinfo, src_coef_arrays);
+ break;
+ case JXFORM_FLIP_V:
+ do_flip_v(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_TRANSPOSE:
+ do_transpose(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_TRANSVERSE:
+ do_transverse(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_ROT_90:
+ do_rot_90(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_ROT_180:
+ do_rot_180(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_ROT_270:
+ do_rot_270(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays);
+ break;
+ }
+}
+
+#endif /* TRANSFORMS_SUPPORTED */
+
+
+/* Setup decompression object to save desired markers in memory.
+ * This must be called before jpeg_read_header() to have the desired effect.
+ */
+
+GLOBAL(void)
+jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option)
+{
+#ifdef SAVE_MARKERS_SUPPORTED
+ int m;
+
+ /* Save comments except under NONE option */
+ if (option != JCOPYOPT_NONE) {
+ jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF);
+ }
+ /* Save all types of APPn markers iff ALL option */
+ if (option == JCOPYOPT_ALL) {
+ for (m = 0; m < 16; m++)
+ jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF);
+ }
+#endif /* SAVE_MARKERS_SUPPORTED */
+}
+
+/* Copy markers saved in the given source object to the destination object.
+ * This should be called just after jpeg_start_compress() or
+ * jpeg_write_coefficients().
+ * Note that those routines will have written the SOI, and also the
+ * JFIF APP0 or Adobe APP14 markers if selected.
+ */
+
+GLOBAL(void)
+jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JCOPY_OPTION option)
+{
+ jpeg_saved_marker_ptr marker;
+
+ /* In the current implementation, we don't actually need to examine the
+ * option flag here; we just copy everything that got saved.
+ * But to avoid confusion, we do not output JFIF and Adobe APP14 markers
+ * if the encoder library already wrote one.
+ */
+ for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (dstinfo->write_JFIF_header &&
+ marker->marker == JPEG_APP0 &&
+ marker->data_length >= 5 &&
+ GETJOCTET(marker->data[0]) == 0x4A &&
+ GETJOCTET(marker->data[1]) == 0x46 &&
+ GETJOCTET(marker->data[2]) == 0x49 &&
+ GETJOCTET(marker->data[3]) == 0x46 &&
+ GETJOCTET(marker->data[4]) == 0)
+ continue; /* reject duplicate JFIF */
+ if (dstinfo->write_Adobe_marker &&
+ marker->marker == JPEG_APP0+14 &&
+ marker->data_length >= 5 &&
+ GETJOCTET(marker->data[0]) == 0x41 &&
+ GETJOCTET(marker->data[1]) == 0x64 &&
+ GETJOCTET(marker->data[2]) == 0x6F &&
+ GETJOCTET(marker->data[3]) == 0x62 &&
+ GETJOCTET(marker->data[4]) == 0x65)
+ continue; /* reject duplicate Adobe */
+#ifdef NEED_FAR_POINTERS
+ /* We could use jpeg_write_marker if the data weren't FAR... */
+ {
+ unsigned int i;
+ jpeg_write_m_header(dstinfo, marker->marker, marker->data_length);
+ for (i = 0; i < marker->data_length; i++)
+ jpeg_write_m_byte(dstinfo, marker->data[i]);
+ }
+#else
+ jpeg_write_marker(dstinfo, marker->marker,
+ marker->data, marker->data_length);
+#endif
+ }
+}
diff --git a/src/imageutils/transupp.h b/src/imageutils/transupp.h
new file mode 100644
index 0000000..c4292b5
--- /dev/null
+++ b/src/imageutils/transupp.h
@@ -0,0 +1,141 @@
+/*
+ * transupp.h
+ *
+ * Copyright (C) 1997, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file contains declarations for image transformation routines and
+ * other utility code used by the jpegtran sample application. These are
+ * NOT part of the core JPEG library. But we keep these routines separate
+ * from jpegtran.c to ease the task of maintaining jpegtran-like programs
+ * that have other user interfaces.
+ *
+ * NOTE: all the routines declared here have very specific requirements
+ * about when they are to be executed during the reading and writing of the
+ * source and destination files. See the comments in transupp.c, or see
+ * jpegtran.c for an example of correct usage.
+ */
+
+#ifndef TRANSUPP_H
+#define TRANSUPP_H
+
+/* If you happen not to want the image transform support, disable it here */
+#ifndef TRANSFORMS_SUPPORTED
+#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */
+#endif
+
+/* Short forms of external names for systems with brain-damaged linkers. */
+
+#ifdef NEED_SHORT_EXTERNAL_NAMES
+#define jtransform_request_workspace jTrRequest
+#define jtransform_adjust_parameters jTrAdjust
+#define jtransform_execute_transformation jTrExec
+#define jcopy_markers_setup jCMrkSetup
+#define jcopy_markers_execute jCMrkExec
+#endif /* NEED_SHORT_EXTERNAL_NAMES */
+
+
+/*
+ * Codes for supported types of image transformations.
+ */
+
+typedef enum {
+ JXFORM_NONE, /* no transformation */
+ JXFORM_FLIP_H, /* horizontal flip */
+ JXFORM_FLIP_V, /* vertical flip */
+ JXFORM_TRANSPOSE, /* transpose across UL-to-LR axis */
+ JXFORM_TRANSVERSE, /* transpose across UR-to-LL axis */
+ JXFORM_ROT_90, /* 90-degree clockwise rotation */
+ JXFORM_ROT_180, /* 180-degree rotation */
+ JXFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) */
+} JXFORM_CODE;
+
+/*
+ * Although rotating and flipping data expressed as DCT coefficients is not
+ * hard, there is an asymmetry in the JPEG format specification for images
+ * whose dimensions aren't multiples of the iMCU size. The right and bottom
+ * image edges are padded out to the next iMCU boundary with junk data; but
+ * no padding is possible at the top and left edges. If we were to flip
+ * the whole image including the pad data, then pad garbage would become
+ * visible at the top and/or left, and real pixels would disappear into the
+ * pad margins --- perhaps permanently, since encoders & decoders may not
+ * bother to preserve DCT blocks that appear to be completely outside the
+ * nominal image area. So, we have to exclude any partial iMCUs from the
+ * basic transformation.
+ *
+ * Transpose is the only transformation that can handle partial iMCUs at the
+ * right and bottom edges completely cleanly. flip_h can flip partial iMCUs
+ * at the bottom, but leaves any partial iMCUs at the right edge untouched.
+ * Similarly flip_v leaves any partial iMCUs at the bottom edge untouched.
+ * The other transforms are defined as combinations of these basic transforms
+ * and process edge blocks in a way that preserves the equivalence.
+ *
+ * The "trim" option causes untransformable partial iMCUs to be dropped;
+ * this is not strictly lossless, but it usually gives the best-looking
+ * result for odd-size images. Note that when this option is active,
+ * the expected mathematical equivalences between the transforms may not hold.
+ * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim
+ * followed by -rot 180 -trim trims both edges.)
+ *
+ * We also offer a "force to grayscale" option, which simply discards the
+ * chrominance channels of a YCbCr image. This is lossless in the sense that
+ * the luminance channel is preserved exactly. It's not the same kind of
+ * thing as the rotate/flip transformations, but it's convenient to handle it
+ * as part of this package, mainly because the transformation routines have to
+ * be aware of the option to know how many components to work on.
+ */
+
+typedef struct {
+ /* Options: set by caller */
+ JXFORM_CODE transform; /* image transform operator */
+ boolean trim; /* if TRUE, trim partial MCUs as needed */
+ boolean force_grayscale; /* if TRUE, convert color image to grayscale */
+
+ /* Internal workspace: caller should not touch these */
+ int num_components; /* # of components in workspace */
+ jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */
+} jpeg_transform_info;
+
+
+#if TRANSFORMS_SUPPORTED
+
+/* Request any required workspace */
+EXTERN(void) jtransform_request_workspace
+ JPP((j_decompress_ptr srcinfo, jpeg_transform_info *info));
+/* Adjust output image parameters */
+EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters
+ JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jpeg_transform_info *info));
+/* Execute the actual transformation, if any */
+EXTERN(void) jtransform_execute_transformation
+ JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jpeg_transform_info *info));
+
+#endif /* TRANSFORMS_SUPPORTED */
+
+
+/*
+ * Support for copying optional markers from source to destination file.
+ */
+
+typedef enum {
+ JCOPYOPT_NONE, /* copy no optional markers */
+ JCOPYOPT_COMMENTS, /* copy only comment (COM) markers */
+ JCOPYOPT_ALL /* copy all optional markers */
+} JCOPY_OPTION;
+
+#define JCOPYOPT_DEFAULT JCOPYOPT_COMMENTS /* recommended default */
+
+/* Setup decompression object to save desired markers in memory */
+EXTERN(void) jcopy_markers_setup
+ JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option));
+/* Copy markers saved in the given source object to the destination object */
+EXTERN(void) jcopy_markers_execute
+ JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JCOPY_OPTION option));
+
+#endif
+