Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/fitz/skew.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/fitz/skew.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,266 @@ +// Copyright (C) 2004-2021 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF 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 Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" + +#include <math.h> +#include <assert.h> +#include <limits.h> + +#if ARCH_HAS_SSE +#include <emmintrin.h> +#include <smmintrin.h> +#endif + +/* Uncomment the following to enable Debugging printfs. */ +/* #define DEBUG_PRINT_WORKING */ + +enum { + NUM_SKEW_COLS = 4, + SKEW_COL_OFFSET = 96, + SKEW_COL_WIDTH = 96, + SKEW_MAX_DIFF = 16 +}; + +typedef struct +{ + void *src; + uint32_t src_w; + uint32_t src_h; + uint32_t y; + uint32_t offsets[NUM_SKEW_COLS*2]; + uint16_t *tables[NUM_SKEW_COLS*2]; + uint32_t corr_height; +} fz_skew; + +/* Finalise a rescaler instance. */ +static void +fz_drop_skew(fz_context *ctx, fz_skew *skew) +{ + if (skew == NULL) + return; + + fz_free(ctx, skew->tables[0]); + fz_free(ctx, skew); +} + +/* Initialise a skew instance. */ +static fz_skew * +fz_new_skew(fz_context *ctx, + unsigned int src_w, + unsigned int src_h) +{ + fz_skew *skew = fz_malloc_struct(ctx, fz_skew); + int i; + + skew->src_w = src_w; + skew->src_h = src_h; + skew->y = 0; + skew->corr_height = src_h-100; /* FIXME */ + fz_try(ctx) + skew->tables[0] = (uint16_t *)fz_malloc(ctx, skew->src_h * sizeof(uint16_t) * NUM_SKEW_COLS * 2); + fz_catch(ctx) + { + fz_drop_skew(ctx, skew); + fz_rethrow(ctx); + } + + for (i = 1; i < NUM_SKEW_COLS * 2; i++) + skew->tables[i] = skew->tables[0] + i*skew->src_h; + + for (i = 0; i < NUM_SKEW_COLS; i++) + { + int offset = skew->src_w * (i+1) / (NUM_SKEW_COLS+1) - SKEW_COL_WIDTH/2; + skew->offsets[2*i ] = offset - SKEW_COL_OFFSET; + skew->offsets[2*i+1] = offset + SKEW_COL_OFFSET; + } + + return skew; +} + +static int sum_c(const uint8_t *data) +{ + int i; + uint32_t sum = 0; + for (i = SKEW_COL_WIDTH; i > 0; i--) + sum += *data++; + return sum; +} + +#if ARCH_HAS_SSE +static inline int sum_sse(const uint8_t *data) +{ + __m128i mm0, mm1, mm2, mm3, mm4, mm5; + __m128i mm_zero = _mm_set1_epi32(0); + + mm0 = _mm_loadu_si128((const __m128i *)(data )); // mm0 = ppoonnmmllkkjjiihhggffeeddccbbaa + mm1 = _mm_loadu_si128((const __m128i *)(data+16)); // mm1 = ppoonnmmllkkjjiihhggffeeddccbbaa + mm2 = _mm_loadu_si128((const __m128i *)(data+32)); // mm2 = ppoonnmmllkkjjiihhggffeeddccbbaa + mm3 = _mm_loadu_si128((const __m128i *)(data+48)); // mm3 = ppoonnmmllkkjjiihhggffeeddccbbaa + mm4 = _mm_loadu_si128((const __m128i *)(data+64)); // mm4 = ppoonnmmllkkjjiihhggffeeddccbbaa + mm5 = _mm_loadu_si128((const __m128i *)(data+80)); // mm5 = ppoonnmmllkkjjiihhggffeeddccbbaa + + mm0 = _mm_sad_epu8(mm0, mm_zero); // Max value in each half is 8*255 + mm1 = _mm_sad_epu8(mm1, mm_zero); + mm2 = _mm_sad_epu8(mm2, mm_zero); + mm3 = _mm_sad_epu8(mm3, mm_zero); + mm4 = _mm_sad_epu8(mm4, mm_zero); + mm5 = _mm_sad_epu8(mm5, mm_zero); + + mm0 = _mm_add_epi64(mm0, mm1); // Max value in each half is 2*8*255 + mm2 = _mm_add_epi64(mm2, mm3); // Max value in each half is 2*8*255 + mm4 = _mm_add_epi64(mm4, mm5); // Max value in each half is 2*8*255 + mm0 = _mm_add_epi64(mm0, mm2); // Max value in each half is 4*8*255 + mm0 = _mm_add_epi64(mm0, mm4); // Max value in each half is 6*8*255 + + mm0 = _mm_shuffle_epi32(mm0, (2<<2)+0); // Max value in each half is 10*8*255 + mm0 = _mm_hadd_epi32(mm0, mm0); // Max value in bottom bits is 20*8*255 - still fits in an unsigned 16bit word. + + return _mm_extract_epi16(mm0, 0); +} +#endif + +/* Process data: */ +static void +fz_skew_process(fz_context *ctx, fz_skew *skew, const uint8_t *input) +{ + int i; + int off = skew->y++; + +#if ARCH_HAS_SSE + for (i = 0; i < NUM_SKEW_COLS * 2; i++) + skew->tables[i][off] = sum_sse(input + skew->offsets[i]); +#else + for (i = 0; i < NUM_SKEW_COLS * 2; i++) + skew->tables[i][off] = sum_c(input + skew->offsets[i]); +#endif + + /* Some debug code; if enabled this writes the summed value back + * in to give us a visual indication of where we are looking for + * correspondences. */ +#if 0 + for (i = 0; i < NUM_SKEW_COLS * 2; i++) { + int v = (skew->tables[i][off] + (SKEW_COL_WIDTH/2) ) / SKEW_COL_WIDTH; + memset(input + skew->offsets[i], v, SKEW_COL_WIDTH); + } +#endif +} + +static double +do_detect_skew(fz_context *ctx, fz_skew *skew) +{ + int i, j, h, o, max_at; + int64_t max_sum, corr_at, corr_sum, avg_sum; + float ang; + int64_t avg = SKEW_COL_WIDTH * 255/2; + + if (skew == NULL) + return 0; + + h = skew->corr_height - 2 * SKEW_MAX_DIFF; + + corr_at = 0; + corr_sum = 0; + for (i = 0; i < NUM_SKEW_COLS; i++) + { + max_at = 9999; + max_sum = 0; + avg_sum = 0; + for (o = -SKEW_MAX_DIFF; o <= SKEW_MAX_DIFF; o++) + { + uint16_t *t0 = skew->tables[2*i] + SKEW_MAX_DIFF; + uint16_t *t1 = skew->tables[2*i+1] + SKEW_MAX_DIFF + o; + int64_t sum = 0; + for (j = 0; j < h; j++) + sum += ((int64_t)t0[j]-avg) * ((int64_t)t1[j]-avg); + if (max_sum < sum) + max_sum = sum, max_at = o; + avg_sum += sum; +#ifdef DEBUG_PRINT_WORKING + printf("col %d, offset %d -> %llx\n", i, o, sum); +#endif + } + avg_sum /= (SKEW_MAX_DIFF+1)*2; +#ifdef DEBUG_PRINT_WORKING + ang = (180.0 / 3.1415942) * atan(max_at / (double)(SKEW_COL_OFFSET * 2)); + printf("col %d max: offset %d -> %llx ang=%g\n", i, max_at, max_sum, ang); +#endif + /* Subtract the average from the maximum; we judge the significance of a + * match by how far it exceeds the average. max_sum becomes 'significance'. */ + max_sum -= avg_sum; +#ifdef DEBUG_PRINT_WORKING + printf("Significance: %llx\n", max_sum - avg_sum); +#endif + corr_at += max_at * max_sum; + corr_sum += max_sum; + } + ang = (180.0 / 3.1415942) * atan(corr_at / (double)(corr_sum * SKEW_COL_OFFSET * 2)); + + if (ang < -45 || ang > 45) + return 0; + + return ang; +} + +double fz_detect_skew(fz_context *ctx, fz_pixmap *pix) +{ + fz_skew *skew = fz_new_skew(ctx, pix->w, pix->h); + int y; + uint8_t *ptr; + ptrdiff_t stride; + double angle; + fz_pixmap *pix2 = NULL; + + fz_var(pix2); + + fz_try(ctx) + { + if (pix->n != 1) + { + pix2 = fz_convert_pixmap(ctx, pix, fz_device_gray(ctx), NULL, NULL, fz_default_color_params, 0); + ptr = pix2->samples; + stride = pix2->stride; + } + else + { + ptr = pix->samples; + stride = pix->stride; + } + + for (y = pix->h; y > 0; y--) + { + fz_skew_process(ctx, skew, ptr); + ptr += stride; + } + + angle = do_detect_skew(ctx, skew); + } + fz_always(ctx) + { + fz_drop_pixmap(ctx, pix2); + fz_drop_skew(ctx, skew); + } + fz_catch(ctx) + fz_rethrow(ctx); + + return angle; +}
