diff --git a/.codacy.yml b/.codacy.yml index 855f04b6e54..8912df30fac 100644 --- a/.codacy.yml +++ b/.codacy.yml @@ -7,4 +7,3 @@ exclude_paths: - test/test.cxx - test/cfg/*.c - test/cfg/*.cpp - - test/synthetic/*.c diff --git a/Makefile b/Makefile index be14a4c1c8a..40856700f68 100644 --- a/Makefile +++ b/Makefile @@ -365,7 +365,7 @@ man/cppcheck.1: $(MAN_SOURCE) $(XP) $(DB2MAN) $(MAN_SOURCE) tags: - ctags -R --exclude=doxyoutput --exclude=test/cfg --exclude=test/synthetic cli externals gui lib test + ctags -R --exclude=doxyoutput --exclude=test/cfg cli externals gui lib test install: cppcheck install -d ${BIN} diff --git a/benchmarks.txt b/benchmarks.txt deleted file mode 100644 index d902aa12996..00000000000 --- a/benchmarks.txt +++ /dev/null @@ -1,22 +0,0 @@ - -========== -Benchmarks -========== - -In this file we can document some good code repos / code samples to use when working on optimisations. - -Trac tickets ------------- - -http://trac.cppcheck.net/ticket/2435 -- Tokenizer::simplifyTypedef -http://trac.cppcheck.net/ticket/8355 -- TokenList::createAst -http://trac.cppcheck.net/ticket/9007 -- Unused types - - -Repos ------ - -Small C++ library with lots of templates: -https://framagit.org/dtschump/CImg -Just check the file examples/use_tinymatwriter.cpp - diff --git a/cve-test-suite/cve-2018-1000618.cpp b/cve-test-suite/cve-2018-1000618.cpp deleted file mode 100644 index 50c7f0e8beb..00000000000 --- a/cve-test-suite/cve-2018-1000618.cpp +++ /dev/null @@ -1,16 +0,0 @@ - -// Reduced source code. Inspired by this fix: -// https://github.com/EOSIO/eos/pull/4112/commits/ef62761c5e388880e8bb1bb41e8b512a5187f255 - -#include - -class C -{ - std::set typedefs; - bool is_type(int type) const - { - if (typedefs.find(type) != typedefs.end()) - return is_type(type); // BUG: endless recursion - return false; - } -}; diff --git a/cve-test-suite/cve-2018-11360.c b/cve-test-suite/cve-2018-11360.c deleted file mode 100644 index c7de11b10a0..00000000000 --- a/cve-test-suite/cve-2018-11360.c +++ /dev/null @@ -1,15 +0,0 @@ - -// CVE: CVE-2018-6836 -// This is a simplified code example based on CVE-2018-11360. - -void *malloc(unsigned long); -void free(void *); - -void f(int size) -{ - char *ia5_string = malloc(size); // Hint: Off by one - for (int i = 0; i <= size; i++) - ia5_string[i]=0; // BUG - free(ia5_string); -} - diff --git a/cve-test-suite/cve-2018-5334.c b/cve-test-suite/cve-2018-5334.c deleted file mode 100644 index 82f3eff4a39..00000000000 --- a/cve-test-suite/cve-2018-5334.c +++ /dev/null @@ -1,9 +0,0 @@ - -// CVE-2018-5334 - -#define LEN 100 - -void f(const int *m_ptr, int sig_off, int rec_size) -{ - if (m_ptr[sig_off] == 0xdd && (sig_off + 15 <= (rec_size - LEN))) {} -} diff --git a/cve-test-suite/cve-2018-6836.c b/cve-test-suite/cve-2018-6836.c deleted file mode 100644 index 5331e131d88..00000000000 --- a/cve-test-suite/cve-2018-6836.c +++ /dev/null @@ -1,30 +0,0 @@ -// Bug: free uninitialized pointer -// Fix: https://code.wireshark.org/review/gitweb?p=wireshark.git;a=commit;h=28960d79cca262ac6b974f339697b299a1e28fef - -void *malloc(unsigned long); -void free(void *); - -struct comment { - int *data; -}; - -struct table { - struct comment *com; -}; - -void destroy_table(struct table *comment_table) -{ - free(comment_table->com->data); - free(comment_table->com); - free(comment_table); -} - -void f() -{ - struct table *comment_table = (struct table *)malloc(sizeof(struct table)); - struct comment *comment_rec = (struct comment *)malloc(sizeof(struct comment)); - comment_table->com = comment_rec; - destroy_table(comment_table); -} - - diff --git a/cve-test-suite/download.sh b/cve-test-suite/download.sh deleted file mode 100755 index a1c9c7206ac..00000000000 --- a/cve-test-suite/download.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# Fetch CVE issues that are interesting to look at - -echo "CVE" > cve.txt - -for i in $(seq 1 20); -do - echo "page $i" - # CVE 119 issues: - # https://www.cvedetails.com/vulnerability-list/cweid-119/vulnerabilities.html - # Use curl to get page $i: - curl -s "https://www.cvedetails.com/vulnerability-list.php?vendor_id=0&product_id=0&version_id=0&page=$i&hasexp=0&opdos=0&opec=0&opov=0&opcsrf=0&opgpriv=0&opsqli=0&opxss=0&opdirt=0&opmemc=0&ophttprs=0&opbyp=0&opfileinc=0&opginf=0&cvssscoremin=0&cvssscoremax=0&year=0&month=0&cweid=119&order=1&trc=11185&sha=a76f56dbb935840fc028b135d550322223547356" > v.html - - # for each cve: - for cve in $(grep /cve/CVE-2018- v.html | sed 's|.*/cve/CVE-2018-\([0-9]*\).*|CVE-2018-\1|'); do - echo "$cve" >> cve.txt - curl -s "https://www.cvedetails.com/cve/$cve/" > download-cve - # cve type - cat download-cve | grep '>Overflow<' >> cve.txt - # is there a code reference? - cat download-cve | grep 'https*://.*[a-f0-9]\{30,50\}' | sed 's|.*\(https*://[^ ]*[a-f0-9]\{30,\}\).*|\1|' >> cve.txt - # is there a pull request reference? - cat download-cve | grep 'https*://github.com/[^ ]*/pull/' | sed 's|.*\(https*://github.com/[^ ]*\).*|\1|' >> cve.txt - done -done - -rm v.html -rm download-cve - - - diff --git a/cve-test-suite/readme.txt b/cve-test-suite/readme.txt deleted file mode 100644 index 479b11dbafb..00000000000 --- a/cve-test-suite/readme.txt +++ /dev/null @@ -1,22 +0,0 @@ - -Background -========== -The CVE database contains known vulnerabilities in various source code projects. For instance, to list known "overflow" vulnerabilities, this link can be used: -https://www.cvedetails.com/vulnerability-list/cweid-119/vulnerabilities.html - -Many issues in the CVE database are "out of reach" for static analysis because of required domain knowledge etc. - -However there are also issues that could be "possible" to detect with static analysis. - -For each such issue that we see that we think is "possible" to detect with static analysis, we can create a file in this folder. The filename is the CVE id. The contents of the file should contain this info: - * Recommended: URL that can be used to download source code, file with bug - * Description - * Reduced example code. The code should be plain C/C++ without dependencies. - -Possible usages: -================ -The test cases can inspire future Cppcheck development. - -These files could be used for a quick and easy tool evaluation. For Cppcheck and other tools. Because only plain C/C++ is used, tools should have all info they need, so hopefully no extra configuration is needed. - -An extended tool evaluation can use the real source code. It's possible to lookup the real source code using the CWE id. However in such tool evaluation, the tools must be configured properly. diff --git a/gui/test/CMakeLists.txt b/gui/test/CMakeLists.txt index ba349e1f8f2..ea868eb97e6 100644 --- a/gui/test/CMakeLists.txt +++ b/gui/test/CMakeLists.txt @@ -1,4 +1,3 @@ -add_subdirectory(benchmark) add_subdirectory(cppchecklibrarydata) add_subdirectory(filelist) add_subdirectory(projectfile) diff --git a/gui/test/benchmark/CMakeLists.txt b/gui/test/benchmark/CMakeLists.txt deleted file mode 100644 index 6b23a955a02..00000000000 --- a/gui/test/benchmark/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(simple) \ No newline at end of file diff --git a/gui/test/benchmark/benchmark.pro b/gui/test/benchmark/benchmark.pro deleted file mode 100644 index 0d3faf3ef33..00000000000 --- a/gui/test/benchmark/benchmark.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += ordered -TEMPLATE = subdirs - -SUBDIRS = simple diff --git a/gui/test/benchmark/simple/CMakeLists.txt b/gui/test/benchmark/simple/CMakeLists.txt deleted file mode 100644 index caf20d2befa..00000000000 --- a/gui/test/benchmark/simple/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -qt_wrap_cpp(test-benchmark-simple_SRC benchmarksimple.h) -add_custom_target(build-testbenchmark-simple-deps SOURCES ${test-benchmark-simple_SRC}) -add_dependencies(gui-build-deps build-testbenchmark-simple-deps) -if(USE_BUNDLED_TINYXML2) - list(APPEND test-benchmark-simple_SRC $) -endif() -add_executable(benchmark-simple - ${test-benchmark-simple_SRC} - benchmarksimple.cpp - $ - $ - ) -target_include_directories(benchmark-simple PRIVATE ${CMAKE_SOURCE_DIR}/lib) -target_compile_definitions(benchmark-simple PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(benchmark-simple ${QT_CORE_LIB} ${QT_TEST_LIB}) -if (HAVE_RULES) - target_link_libraries(benchmark-simple ${PCRE_LIBRARY}) -endif() -if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2) - target_link_libraries(benchmark-simple ${tinyxml2_LIBRARIES}) -endif() - -if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14) - # false positive in up to CLang 13 - caused by QBENCHMARK macro - set_source_files_properties(benchmarksimple.cpp PROPERTIES COMPILE_FLAGS -Wno-reserved-identifier) - endif() - # caused by Q_UNUSED macro - set_source_files_properties(moc_benchmarksimple.cpp PROPERTIES COMPILE_FLAGS -Wno-extra-semi-stmt) -endif() diff --git a/gui/test/benchmark/simple/benchmarksimple.cpp b/gui/test/benchmark/simple/benchmarksimple.cpp deleted file mode 100644 index a808ae093de..00000000000 --- a/gui/test/benchmark/simple/benchmarksimple.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2021 Cppcheck team. - * - * 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 3 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, see . - */ - -#include "benchmarksimple.h" - -#include "settings.h" -#include "tokenize.h" - -#include - -#include -#include -#include -#include - -void BenchmarkSimple::tokenize() -{ - QFile file(QString(SRCDIR) + "/../../data/benchmark/simple.cpp"); - QByteArray data = file.readAll(); - - Settings settings; - settings.debugwarnings = true; - - // tokenize.. - Tokenizer tokenizer(&settings, this); - std::istringstream istr(data.constData()); - QBENCHMARK { - tokenizer.tokenize(istr, "test.cpp"); - } -} - -QTEST_MAIN(BenchmarkSimple) diff --git a/gui/test/benchmark/simple/benchmarksimple.h b/gui/test/benchmark/simple/benchmarksimple.h deleted file mode 100644 index 87bf8b941d0..00000000000 --- a/gui/test/benchmark/simple/benchmarksimple.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2021 Cppcheck team. - * - * 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 3 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, see . - */ - -#include "color.h" -#include "errorlogger.h" - -#include - -#include - -class BenchmarkSimple : public QObject, public ErrorLogger { - Q_OBJECT - -private slots: - void tokenize(); - -private: - // Empty implementations of ErrorLogger methods. - // We don't care about the output in the benchmark tests. - void reportOut(const std::string & /*outmsg*/, Color /*c*/ = Color::Reset) override {} - void reportErr(const ErrorMessage & /*msg*/) override {} -}; diff --git a/gui/test/benchmark/simple/simple.pro b/gui/test/benchmark/simple/simple.pro deleted file mode 100644 index b5bd4f85438..00000000000 --- a/gui/test/benchmark/simple/simple.pro +++ /dev/null @@ -1,15 +0,0 @@ -TEMPLATE = app -TARGET = benchmark-simple -DEPENDPATH += . -INCLUDEPATH += . -OBJECTS_DIR = ../../../temp -MOC_DIR = ../../temp - -include(../../common.pri) - -DEFINES += SRCDIR=\\\"$$PWD\\\" - -# tests -SOURCES += benchmarksimple.cpp - -HEADERS += benchmarksimple.h diff --git a/gui/test/readme.txt b/gui/test/readme.txt index 4421bc02fd9..b02dcbcab87 100644 --- a/gui/test/readme.txt +++ b/gui/test/readme.txt @@ -1,4 +1,4 @@ -GUI tests + benchmark tests +GUI tests =========================== As the GUI uses Qt framework, the GUI tests also use Qt's Testlib. This is diff --git a/htmlreport/check.sh b/htmlreport/check.sh index 577dbbd8d12..a4515ec0690 100755 --- a/htmlreport/check.sh +++ b/htmlreport/check.sh @@ -40,7 +40,7 @@ validate_html "$INDEX_HTML" validate_html "$STATS_HTML" -../cppcheck ../test/synthetic --enable=all --inconclusive --xml-version=2 2> "$GUI_TEST_XML" +../cppcheck ../samples --enable=all --inconclusive --xml-version=2 2> "$GUI_TEST_XML" xmllint --noout "$GUI_TEST_XML" $PYTHON cppcheck-htmlreport --file "$GUI_TEST_XML" --title "xml2 + inconclusive test" --report-dir "$REPORT_DIR" echo "" @@ -49,7 +49,7 @@ validate_html "$INDEX_HTML" validate_html "$STATS_HTML" -../cppcheck ../test/synthetic --enable=all --inconclusive --verbose --xml-version=2 2> "$GUI_TEST_XML" +../cppcheck ../samples --enable=all --inconclusive --verbose --xml-version=2 2> "$GUI_TEST_XML" xmllint --noout "$GUI_TEST_XML" $PYTHON cppcheck-htmlreport --file "$GUI_TEST_XML" --title "xml2 + inconclusive + verbose test" --report-dir "$REPORT_DIR" echo -e "\n" diff --git a/test/bug-hunting/cve.py b/test/bug-hunting/cve.py deleted file mode 100644 index b81ec546c9f..00000000000 --- a/test/bug-hunting/cve.py +++ /dev/null @@ -1,73 +0,0 @@ -# Test if --bug-hunting works using cve tests - -import glob -import logging -import os -import sys -import subprocess - -if sys.argv[0] in ('test/bug-hunting/cve.py', './test/bug-hunting/cve.py'): - CPPCHECK_PATH = './cppcheck' - TEST_SUITE = 'test/bug-hunting/cve' -else: - CPPCHECK_PATH = '../../cppcheck' - TEST_SUITE = 'cve' - -slow = '--slow' in sys.argv - -logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', datefmt='%H:%M:%S') - -def test(test_folder): - logging.info(test_folder) - - cmd_file = os.path.join(test_folder, 'cmd.txt') - expected_file = os.path.join(test_folder, 'expected.txt') - - cmd = ['nice', - CPPCHECK_PATH, - '-D__GNUC__', - '--bug-hunting', - '--inconclusive', - '--platform=unix64', - '--template={file}:{line}:{id}', - '-rp=' + test_folder] - - if os.path.isfile(cmd_file): - for line in open(cmd_file, 'rt'): - if len(line) > 1: - cmd.append(line.strip()) - - cmd.append(test_folder) - - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - comm = p.communicate() - stdout = comm[0].decode(encoding='utf-8', errors='ignore') - stderr = comm[1].decode(encoding='utf-8', errors='ignore') - - with open(expected_file, 'rt') as f: - for expected in f.readlines(): - if expected.strip() not in stderr.split('\n'): - print('FAILED. Expected result not found: ' + expected) - print('Command:') - print(' '.join(cmd)) - print('Output:') - print(stderr) - sys.exit(1) - -if (slow is False) and len(sys.argv) > 1: - test(sys.argv[1]) - sys.exit(0) - -SLOW = [] - -for test_folder in sorted(glob.glob(TEST_SUITE + '/CVE*')): - if slow is False: - check = False - for s in SLOW: - if s in test_folder: - check = True - if check is True: - logging.info('skipping %s', test_folder) - continue - test(test_folder) - diff --git a/test/bug-hunting/cve/CVE-2018-19872/README b/test/bug-hunting/cve/CVE-2018-19872/README deleted file mode 100644 index b7bf53290ba..00000000000 --- a/test/bug-hunting/cve/CVE-2018-19872/README +++ /dev/null @@ -1,6 +0,0 @@ -Project: -Qt - -Details: -https://nvd.nist.gov/vuln/detail/CVE-2018-19872 - diff --git a/test/bug-hunting/cve/CVE-2018-19872/expected.txt b/test/bug-hunting/cve/CVE-2018-19872/expected.txt deleted file mode 100644 index 07ba035caba..00000000000 --- a/test/bug-hunting/cve/CVE-2018-19872/expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -qppmhandler.cpp:223:bughuntingDivByZero -qppmhandler.cpp:255:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2018-19872/qppmhandler.cpp b/test/bug-hunting/cve/CVE-2018-19872/qppmhandler.cpp deleted file mode 100644 index b80ebcf9cf8..00000000000 --- a/test/bug-hunting/cve/CVE-2018-19872/qppmhandler.cpp +++ /dev/null @@ -1,579 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "private/qppmhandler_p.h" - -#ifndef QT_NO_IMAGEFORMAT_PPM - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -/***************************************************************************** - PBM/PGM/PPM (ASCII and RAW) image read/write functions -*****************************************************************************/ - -static void discard_pbm_line(QIODevice *d) -{ - const int buflen = 100; - char buf[buflen]; - int res = 0; - do { - res = d->readLine(buf, buflen); - } while (res > 0 && buf[res-1] != '\n'); -} - -static int read_pbm_int(QIODevice *d) -{ - char c; - int val = -1; - bool digit; - for (;;) { - if (!d->getChar(&c)) // end of file - break; - digit = isdigit((uchar) c); - if (val != -1) { - if (digit) { - val = 10*val + c - '0'; - continue; - } else { - if (c == '#') // comment - discard_pbm_line(d); - break; - } - } - if (digit) // first digit - val = c - '0'; - else if (isspace((uchar) c)) - continue; - else if (c == '#') - discard_pbm_line(d); - else - break; - } - return val; -} - -static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int& mcc) -{ - char buf[3]; - if (device->read(buf, 3) != 3) // read P[1-6] - return false; - - if (!(buf[0] == 'P' && isdigit((uchar) buf[1]) && isspace((uchar) buf[2]))) - return false; - - type = buf[1]; - if (type < '1' || type > '6') - return false; - - w = read_pbm_int(device); // get image width - h = read_pbm_int(device); // get image height - - if (type == '1' || type == '4') - mcc = 1; // ignore max color component - else - mcc = read_pbm_int(device); // get max color component - - if (w <= 0 || w > 32767 || h <= 0 || h > 32767 || mcc <= 0) - return false; // weird P.M image - - return true; -} - -static inline QRgb scale_pbm_color(quint16 mx, quint16 rv, quint16 gv, quint16 bv) -{ - return QRgba64::fromRgba64((rv * 0xffff) / mx, (gv * 0xffff) / mx, (bv * 0xffff) / mx, 0xffff).toArgb32(); -} - -static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage) -{ - int nbits, y; - int pbm_bpl; - bool raw; - - QImage::Format format; - switch (type) { - case '1': // ascii PBM - case '4': // raw PBM - nbits = 1; - format = QImage::Format_Mono; - break; - case '2': // ascii PGM - case '5': // raw PGM - nbits = 8; - format = QImage::Format_Grayscale8; - break; - case '3': // ascii PPM - case '6': // raw PPM - nbits = 32; - format = QImage::Format_RGB32; - break; - default: - return false; - } - raw = type >= '4'; - - if (outImage->size() != QSize(w, h) || outImage->format() != format) { - *outImage = QImage(w, h, format); - if (outImage->isNull()) - return false; - } - - pbm_bpl = (nbits*w+7)/8; // bytes per scanline in PBM - - if (raw) { // read raw data - if (nbits == 32) { // type 6 - pbm_bpl = mcc < 256 ? 3*w : 6*w; - uchar *buf24 = new uchar[pbm_bpl], *b; - QRgb *p; - QRgb *end; - for (y=0; yread((char *)buf24, pbm_bpl) != pbm_bpl) { - delete[] buf24; - return false; - } - p = (QRgb *)outImage->scanLine(y); - end = p + w; - b = buf24; - while (p < end) { - if (mcc < 256) { - if (mcc == 255) - *p++ = qRgb(b[0],b[1],b[2]); - else - *p++ = scale_pbm_color(mcc, b[0], b[1], b[2]); - b += 3; - } else { - quint16 rv = b[0] << 8 | b[1]; - quint16 gv = b[2] << 8 | b[3]; - quint16 bv = b[4] << 8 | b[5]; - if (mcc == 0xffff) - *p++ = QRgba64::fromRgba64(rv, gv, bv, 0xffff).toArgb32(); - else - *p++ = scale_pbm_color(mcc, rv, gv, bv); - b += 6; - } - } - } - delete[] buf24; - } else if (nbits == 8 && mcc > 255) { // type 5 16bit - pbm_bpl = 2*w; - uchar *buf16 = new uchar[pbm_bpl]; - for (y=0; yread((char *)buf16, pbm_bpl) != pbm_bpl) { - delete[] buf16; - return false; - } - uchar *p = outImage->scanLine(y); - uchar *end = p + w; - uchar *b = buf16; - while (p < end) { - *p++ = (b[0] << 8 | b[1]) * 255 / mcc; - b += 2; - } - } - delete[] buf16; - } else { // type 4,5 - for (y=0; yscanLine(y); - if (device->read((char *)p, pbm_bpl) != pbm_bpl) - return false; - if (nbits == 8 && mcc < 255) { - for (int i = 0; i < pbm_bpl; i++) - p[i] = (p[i] * 255) / mcc; - } - } - } - } else { // read ascii data - uchar *p; - int n; - char buf; - for (y = 0; (y < h) && (device->peek(&buf, 1) == 1); y++) { - p = outImage->scanLine(y); - n = pbm_bpl; - if (nbits == 1) { - int b; - int bitsLeft = w; - while (n--) { - b = 0; - for (int i=0; i<8; i++) { - if (i < bitsLeft) - b = (b << 1) | (read_pbm_int(device) & 1); - else - b = (b << 1) | (0 & 1); // pad it our self if we need to - } - bitsLeft -= 8; - *p++ = b; - } - } else if (nbits == 8) { - if (mcc == 255) { - while (n--) { - *p++ = read_pbm_int(device); - } - } else { - while (n--) { - *p++ = read_pbm_int(device) * 255 / mcc; - } - } - } else { // 32 bits - n /= 4; - int r, g, b; - if (mcc == 255) { - while (n--) { - r = read_pbm_int(device); - g = read_pbm_int(device); - b = read_pbm_int(device); - *((QRgb*)p) = qRgb(r, g, b); - p += 4; - } - } else { - while (n--) { - r = read_pbm_int(device); - g = read_pbm_int(device); - b = read_pbm_int(device); - *((QRgb*)p) = scale_pbm_color(mcc, r, g, b); - p += 4; - } - } - } - } - } - - if (format == QImage::Format_Mono) { - outImage->setColorCount(2); - outImage->setColor(0, qRgb(255,255,255)); // white - outImage->setColor(1, qRgb(0,0,0)); // black - } - - return true; -} - -static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QByteArray &sourceFormat) -{ - QByteArray str; - QImage image = sourceImage; - QByteArray format = sourceFormat; - - format = format.left(3); // ignore RAW part - bool gray = format == "pgm"; - - if (format == "pbm") { - image = image.convertToFormat(QImage::Format_Mono); - } else if (gray) { - image = image.convertToFormat(QImage::Format_Grayscale8); - } else { - switch (image.format()) { - case QImage::Format_Mono: - case QImage::Format_MonoLSB: - image = image.convertToFormat(QImage::Format_Indexed8); - break; - case QImage::Format_Indexed8: - case QImage::Format_RGB32: - case QImage::Format_ARGB32: - break; - default: - if (image.hasAlphaChannel()) - image = image.convertToFormat(QImage::Format_ARGB32); - else - image = image.convertToFormat(QImage::Format_RGB32); - break; - } - } - - if (image.depth() == 1 && image.colorCount() == 2) { - if (qGray(image.color(0)) < qGray(image.color(1))) { - // 0=dark/black, 1=light/white - invert - image.detach(); - for (int y=0; ywrite(str, str.length()) != str.length()) - return false; - w = (w+7)/8; - for (uint y=0; ywrite((char*)line, w)) - return false; - } - } - break; - - case 8: { - str.insert(1, gray ? '5' : '6'); - str.append("255\n"); - if (out->write(str, str.length()) != str.length()) - return false; - uint bpl = w * (gray ? 1 : 3); - uchar *buf = new uchar[bpl]; - if (image.format() == QImage::Format_Indexed8) { - QVector color = image.colorTable(); - for (uint y=0; ywrite((char*)buf, bpl)) - return false; - } - } else { - for (uint y=0; ywrite((char*)buf, bpl)) - return false; - } - } - delete[] buf; - break; - } - - case 32: { - str.insert(1, '6'); - str.append("255\n"); - if (out->write(str, str.length()) != str.length()) - return false; - uint bpl = w * 3; - uchar *buf = new uchar[bpl]; - for (uint y=0; y(image.constScanLine(y)); - uchar *p = buf; - uchar *end = buf+bpl; - while (p < end) { - QRgb rgb = *b++; - *p++ = qRed(rgb); - *p++ = qGreen(rgb); - *p++ = qBlue(rgb); - } - if (bpl != (uint)out->write((char*)buf, bpl)) - return false; - } - delete[] buf; - break; - } - - default: - return false; - } - - return true; -} - -QPpmHandler::QPpmHandler() - : state(Ready) -{} - -bool QPpmHandler::readHeader() -{ - state = Error; - if (!read_pbm_header(device(), type, width, height, mcc)) - return false; - state = ReadHeader; - return true; -} - -bool QPpmHandler::canRead() const -{ - if (state == Ready && !canRead(device(), &subType)) - return false; - - if (state != Error) { - setFormat(subType); - return true; - } - - return false; -} - -bool QPpmHandler::canRead(QIODevice *device, QByteArray *subType) -{ - if (!device) { - qWarning("QPpmHandler::canRead() called with no device"); - return false; - } - - char head[2]; - if (device->peek(head, sizeof(head)) != sizeof(head)) - return false; - - if (head[0] != 'P') - return false; - - if (head[1] == '1' || head[1] == '4') { - if (subType) - *subType = "pbm"; - } else if (head[1] == '2' || head[1] == '5') { - if (subType) - *subType = "pgm"; - } else if (head[1] == '3' || head[1] == '6') { - if (subType) - *subType = "ppm"; - } else { - return false; - } - return true; -} - -bool QPpmHandler::read(QImage *image) -{ - if (state == Error) - return false; - - if (state == Ready && !readHeader()) { - state = Error; - return false; - } - - if (!read_pbm_body(device(), type, width, height, mcc, image)) { - state = Error; - return false; - } - - state = Ready; - return true; -} - -bool QPpmHandler::write(const QImage &image) -{ - return write_pbm_image(device(), image, subType); -} - -bool QPpmHandler::supportsOption(ImageOption option) const -{ - return option == SubType - || option == Size - || option == ImageFormat; -} - -QVariant QPpmHandler::option(ImageOption option) const -{ - if (option == SubType) { - return subType; - } else if (option == Size) { - if (state == Error) - return QVariant(); - if (state == Ready && !const_cast(this)->readHeader()) - return QVariant(); - return QSize(width, height); - } else if (option == ImageFormat) { - if (state == Error) - return QVariant(); - if (state == Ready && !const_cast(this)->readHeader()) - return QVariant(); - QImage::Format format = QImage::Format_Invalid; - switch (type) { - case '1': // ascii PBM - case '4': // raw PBM - format = QImage::Format_Mono; - break; - case '2': // ascii PGM - case '5': // raw PGM - format = QImage::Format_Grayscale8; - break; - case '3': // ascii PPM - case '6': // raw PPM - format = QImage::Format_RGB32; - break; - default: - break; - } - return format; - } - return QVariant(); -} - -void QPpmHandler::setOption(ImageOption option, const QVariant &value) -{ - if (option == SubType) - subType = value.toByteArray().toLower(); -} - -QByteArray QPpmHandler::name() const -{ - return subType.isEmpty() ? QByteArray("ppm") : subType; -} - -QT_END_NAMESPACE - -#endif // QT_NO_IMAGEFORMAT_PPM diff --git a/test/bug-hunting/cve/CVE-2018-20845/expected.txt b/test/bug-hunting/cve/CVE-2018-20845/expected.txt deleted file mode 100644 index f94c37bcb80..00000000000 --- a/test/bug-hunting/cve/CVE-2018-20845/expected.txt +++ /dev/null @@ -1,3 +0,0 @@ -pi.c:426:bughuntingDivByZero -pi.c:430:bughuntingDivByZero - diff --git a/test/bug-hunting/cve/CVE-2018-20845/pi.c b/test/bug-hunting/cve/CVE-2018-20845/pi.c deleted file mode 100644 index 5edb2223de4..00000000000 --- a/test/bug-hunting/cve/CVE-2018-20845/pi.c +++ /dev/null @@ -1,1050 +0,0 @@ -/* - * The copyright in this software is being made available under the 2-clauses - * BSD License, included below. This software may be subject to other third - * party and contributor rights, including patent rights, and no such rights - * are granted under this license. - * - * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium - * Copyright (c) 2002-2014, Professor Benoit Macq - * Copyright (c) 2001-2003, David Janssens - * Copyright (c) 2002-2003, Yannick Verschueren - * Copyright (c) 2003-2007, Francois-Olivier Devaux - * Copyright (c) 2003-2014, Antonin Descampe - * Copyright (c) 2005, Herve Drolon, FreeImage Team - * Copyright (c) 2006-2007, Parvatha Elangovan - * All rights reserved. - * - * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS `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 COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#include "opj_includes.h" - -/** @defgroup PI PI - Implementation of a packet iterator */ -/*@{*/ - -/** @name Local static functions */ -/*@{*/ - -/** - Get next packet in layer-resolution-component-precinct order. - @param pi packet iterator to modify - @return returns false if pi pointed to the last packet or else returns true - */ -static opj_bool pi_next_lrcp(opj_pi_iterator_t * pi); -/** - Get next packet in resolution-layer-component-precinct order. - @param pi packet iterator to modify - @return returns false if pi pointed to the last packet or else returns true - */ -static opj_bool pi_next_rlcp(opj_pi_iterator_t * pi); -/** - Get next packet in resolution-precinct-component-layer order. - @param pi packet iterator to modify - @return returns false if pi pointed to the last packet or else returns true - */ -static opj_bool pi_next_rpcl(opj_pi_iterator_t * pi); -/** - Get next packet in precinct-component-resolution-layer order. - @param pi packet iterator to modify - @return returns false if pi pointed to the last packet or else returns true - */ -static opj_bool pi_next_pcrl(opj_pi_iterator_t * pi); -/** - Get next packet in component-precinct-resolution-layer order. - @param pi packet iterator to modify - @return returns false if pi pointed to the last packet or else returns true - */ -static opj_bool pi_next_cprl(opj_pi_iterator_t * pi); - -/*@}*/ - -/*@}*/ - -/* - ========================================================== - local functions - ========================================================== - */ - -static opj_bool pi_next_lrcp(opj_pi_iterator_t * pi) -{ - opj_pi_comp_t *comp = NULL; - opj_pi_resolution_t *res = NULL; - long index = 0; - - if (!pi->first) { - comp = &pi->comps[pi->compno]; - res = &comp->resolutions[pi->resno]; - goto LABEL_SKIP; - } else { - pi->first = 0; - } - - for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) { - for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1; - pi->resno++) { - for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) { - comp = &pi->comps[pi->compno]; - if (pi->resno >= comp->numresolutions) { - continue; - } - res = &comp->resolutions[pi->resno]; - if (!pi->tp_on) { - pi->poc.precno1 = res->pw * res->ph; - } - for (pi->precno = pi->poc.precno0; pi->precno < pi->poc.precno1; pi->precno++) { - index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno * - pi->step_c + pi->precno * pi->step_p; - if (!pi->include[index]) { - pi->include[index] = 1; - return OPJ_TRUE; - } -LABEL_SKIP: - ; - } - } - } - } - - return OPJ_FALSE; -} - -static opj_bool pi_next_rlcp(opj_pi_iterator_t * pi) -{ - opj_pi_comp_t *comp = NULL; - opj_pi_resolution_t *res = NULL; - long index = 0; - - if (!pi->first) { - comp = &pi->comps[pi->compno]; - res = &comp->resolutions[pi->resno]; - goto LABEL_SKIP; - } else { - pi->first = 0; - } - - for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1; pi->resno++) { - for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) { - for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) { - comp = &pi->comps[pi->compno]; - if (pi->resno >= comp->numresolutions) { - continue; - } - res = &comp->resolutions[pi->resno]; - if (!pi->tp_on) { - pi->poc.precno1 = res->pw * res->ph; - } - for (pi->precno = pi->poc.precno0; pi->precno < pi->poc.precno1; pi->precno++) { - index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno * - pi->step_c + pi->precno * pi->step_p; - if (!pi->include[index]) { - pi->include[index] = 1; - return OPJ_TRUE; - } -LABEL_SKIP: - ; - } - } - } - } - - return OPJ_FALSE; -} - -static opj_bool pi_next_rpcl(opj_pi_iterator_t * pi) -{ - opj_pi_comp_t *comp = NULL; - opj_pi_resolution_t *res = NULL; - long index = 0; - - if (!pi->first) { - goto LABEL_SKIP; - } else { - int compno, resno; - pi->first = 0; - pi->dx = 0; - pi->dy = 0; - for (compno = 0; compno < pi->numcomps; compno++) { - comp = &pi->comps[compno]; - for (resno = 0; resno < comp->numresolutions; resno++) { - int dx, dy; - res = &comp->resolutions[resno]; - dx = comp->dx * (1 << (res->pdx + comp->numresolutions - 1 - resno)); - dy = comp->dy * (1 << (res->pdy + comp->numresolutions - 1 - resno)); - pi->dx = !pi->dx ? dx : int_min(pi->dx, dx); - pi->dy = !pi->dy ? dy : int_min(pi->dy, dy); - } - } - } - if (!pi->tp_on) { - pi->poc.ty0 = pi->ty0; - pi->poc.tx0 = pi->tx0; - pi->poc.ty1 = pi->ty1; - pi->poc.tx1 = pi->tx1; - } - for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1; pi->resno++) { - for (pi->y = pi->poc.ty0; pi->y < pi->poc.ty1; - pi->y += pi->dy - (pi->y % pi->dy)) { - for (pi->x = pi->poc.tx0; pi->x < pi->poc.tx1; - pi->x += pi->dx - (pi->x % pi->dx)) { - for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) { - int levelno; - int trx0, try0; - int trx1, try1; - int rpx, rpy; - int prci, prcj; - comp = &pi->comps[pi->compno]; - if (pi->resno >= comp->numresolutions) { - continue; - } - res = &comp->resolutions[pi->resno]; - levelno = comp->numresolutions - 1 - pi->resno; - trx0 = int_ceildiv(pi->tx0, comp->dx << levelno); - try0 = int_ceildiv(pi->ty0, comp->dy << levelno); - trx1 = int_ceildiv(pi->tx1, comp->dx << levelno); - try1 = int_ceildiv(pi->ty1, comp->dy << levelno); - rpx = res->pdx + levelno; - rpy = res->pdy + levelno; - - /* To avoid divisions by zero / undefined behaviour on shift */ - if (rpx >= 31 || ((comp->dx << rpx) >> rpx) != comp->dx || - rpy >= 31 || ((comp->dy << rpy) >> rpy) != comp->dy) { - continue; - } - - if (!((pi->y % (comp->dy << rpy) == 0) || ((pi->y == pi->ty0) && - ((try0 << levelno) % (1 << rpy))))) { - continue; - } - if (!((pi->x % (comp->dx << rpx) == 0) || ((pi->x == pi->tx0) && - ((trx0 << levelno) % (1 << rpx))))) { - continue; - } - - if ((res->pw == 0) || (res->ph == 0)) { - continue; - } - - if ((trx0 == trx1) || (try0 == try1)) { - continue; - } - - prci = int_floordivpow2(int_ceildiv(pi->x, comp->dx << levelno), res->pdx) - - int_floordivpow2(trx0, res->pdx); - prcj = int_floordivpow2(int_ceildiv(pi->y, comp->dy << levelno), res->pdy) - - int_floordivpow2(try0, res->pdy); - pi->precno = prci + prcj * res->pw; - for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) { - index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno * - pi->step_c + pi->precno * pi->step_p; - if (!pi->include[index]) { - pi->include[index] = 1; - return OPJ_TRUE; - } -LABEL_SKIP: - ; - } - } - } - } - } - - return OPJ_FALSE; -} - -static opj_bool pi_next_pcrl(opj_pi_iterator_t * pi) -{ - opj_pi_comp_t *comp = NULL; - opj_pi_resolution_t *res = NULL; - long index = 0; - - if (!pi->first) { - comp = &pi->comps[pi->compno]; - goto LABEL_SKIP; - } else { - int compno, resno; - pi->first = 0; - pi->dx = 0; - pi->dy = 0; - for (compno = 0; compno < pi->numcomps; compno++) { - comp = &pi->comps[compno]; - for (resno = 0; resno < comp->numresolutions; resno++) { - int dx, dy; - res = &comp->resolutions[resno]; - dx = comp->dx * (1 << (res->pdx + comp->numresolutions - 1 - resno)); - dy = comp->dy * (1 << (res->pdy + comp->numresolutions - 1 - resno)); - pi->dx = !pi->dx ? dx : int_min(pi->dx, dx); - pi->dy = !pi->dy ? dy : int_min(pi->dy, dy); - } - } - } - if (!pi->tp_on) { - pi->poc.ty0 = pi->ty0; - pi->poc.tx0 = pi->tx0; - pi->poc.ty1 = pi->ty1; - pi->poc.tx1 = pi->tx1; - } - for (pi->y = pi->poc.ty0; pi->y < pi->poc.ty1; - pi->y += pi->dy - (pi->y % pi->dy)) { - for (pi->x = pi->poc.tx0; pi->x < pi->poc.tx1; - pi->x += pi->dx - (pi->x % pi->dx)) { - for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) { - comp = &pi->comps[pi->compno]; - for (pi->resno = pi->poc.resno0; - pi->resno < int_min(pi->poc.resno1, comp->numresolutions); pi->resno++) { - int levelno; - int trx0, try0; - int trx1, try1; - int rpx, rpy; - int prci, prcj; - res = &comp->resolutions[pi->resno]; - levelno = comp->numresolutions - 1 - pi->resno; - trx0 = int_ceildiv(pi->tx0, comp->dx << levelno); - try0 = int_ceildiv(pi->ty0, comp->dy << levelno); - trx1 = int_ceildiv(pi->tx1, comp->dx << levelno); - try1 = int_ceildiv(pi->ty1, comp->dy << levelno); - rpx = res->pdx + levelno; - rpy = res->pdy + levelno; - - /* To avoid divisions by zero / undefined behaviour on shift */ - if (rpx >= 31 || ((comp->dx << rpx) >> rpx) != comp->dx || - rpy >= 31 || ((comp->dy << rpy) >> rpy) != comp->dy) { - continue; - } - - if (!((pi->y % (comp->dy << rpy) == 0) || ((pi->y == pi->ty0) && - ((try0 << levelno) % (1 << rpy))))) { - continue; - } - if (!((pi->x % (comp->dx << rpx) == 0) || ((pi->x == pi->tx0) && - ((trx0 << levelno) % (1 << rpx))))) { - continue; - } - - if ((res->pw == 0) || (res->ph == 0)) { - continue; - } - - if ((trx0 == trx1) || (try0 == try1)) { - continue; - } - - prci = int_floordivpow2(int_ceildiv(pi->x, comp->dx << levelno), res->pdx) - - int_floordivpow2(trx0, res->pdx); - prcj = int_floordivpow2(int_ceildiv(pi->y, comp->dy << levelno), res->pdy) - - int_floordivpow2(try0, res->pdy); - pi->precno = prci + prcj * res->pw; - for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) { - index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno * - pi->step_c + pi->precno * pi->step_p; - if (!pi->include[index]) { - pi->include[index] = 1; - return OPJ_TRUE; - } -LABEL_SKIP: - ; - } - } - } - } - } - - return OPJ_FALSE; -} - -static opj_bool pi_next_cprl(opj_pi_iterator_t * pi) -{ - opj_pi_comp_t *comp = NULL; - opj_pi_resolution_t *res = NULL; - long index = 0; - - if (!pi->first) { - comp = &pi->comps[pi->compno]; - goto LABEL_SKIP; - } else { - pi->first = 0; - } - - for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) { - int resno; - comp = &pi->comps[pi->compno]; - pi->dx = 0; - pi->dy = 0; - for (resno = 0; resno < comp->numresolutions; resno++) { - int dx, dy; - res = &comp->resolutions[resno]; - dx = comp->dx * (1 << (res->pdx + comp->numresolutions - 1 - resno)); - dy = comp->dy * (1 << (res->pdy + comp->numresolutions - 1 - resno)); - pi->dx = !pi->dx ? dx : int_min(pi->dx, dx); - pi->dy = !pi->dy ? dy : int_min(pi->dy, dy); - } - if (!pi->tp_on) { - pi->poc.ty0 = pi->ty0; - pi->poc.tx0 = pi->tx0; - pi->poc.ty1 = pi->ty1; - pi->poc.tx1 = pi->tx1; - } - for (pi->y = pi->poc.ty0; pi->y < pi->poc.ty1; - pi->y += pi->dy - (pi->y % pi->dy)) { - for (pi->x = pi->poc.tx0; pi->x < pi->poc.tx1; - pi->x += pi->dx - (pi->x % pi->dx)) { - for (pi->resno = pi->poc.resno0; - pi->resno < int_min(pi->poc.resno1, comp->numresolutions); pi->resno++) { - int levelno; - int trx0, try0; - int trx1, try1; - int rpx, rpy; - int prci, prcj; - res = &comp->resolutions[pi->resno]; - levelno = comp->numresolutions - 1 - pi->resno; - trx0 = int_ceildiv(pi->tx0, comp->dx << levelno); - try0 = int_ceildiv(pi->ty0, comp->dy << levelno); - trx1 = int_ceildiv(pi->tx1, comp->dx << levelno); - try1 = int_ceildiv(pi->ty1, comp->dy << levelno); - rpx = res->pdx + levelno; - rpy = res->pdy + levelno; - - if (!((pi->y % (comp->dy << rpy) == 0) || ((pi->y == pi->ty0) && - ((try0 << levelno) % (1 << rpy))))) { - continue; - } - if (!((pi->x % (comp->dx << rpx) == 0) || ((pi->x == pi->tx0) && - ((trx0 << levelno) % (1 << rpx))))) { - continue; - } - - if ((res->pw == 0) || (res->ph == 0)) { - continue; - } - - if ((trx0 == trx1) || (try0 == try1)) { - continue; - } - - prci = int_floordivpow2(int_ceildiv(pi->x, comp->dx << levelno), res->pdx) - - int_floordivpow2(trx0, res->pdx); - prcj = int_floordivpow2(int_ceildiv(pi->y, comp->dy << levelno), res->pdy) - - int_floordivpow2(try0, res->pdy); - pi->precno = prci + prcj * res->pw; - for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) { - index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno * - pi->step_c + pi->precno * pi->step_p; - if (!pi->include[index]) { - pi->include[index] = 1; - return OPJ_TRUE; - } -LABEL_SKIP: - ; - } - } - } - } - } - - return OPJ_FALSE; -} - -/* - ========================================================== - Packet iterator interface - ========================================================== - */ - -opj_pi_iterator_t *pi_create_decode(opj_image_t *image, opj_cp_t *cp, - int tileno) -{ - int p, q; - int compno, resno, pino; - opj_pi_iterator_t *pi = NULL; - opj_tcp_t *tcp = NULL; - opj_tccp_t *tccp = NULL; - - tcp = &cp->tcps[tileno]; - - pi = (opj_pi_iterator_t*) opj_calloc((tcp->numpocs + 1), - sizeof(opj_pi_iterator_t)); - if (!pi) { - /* TODO: throw an error */ - return NULL; - } - - for (pino = 0; pino < tcp->numpocs + 1; pino++) { /* change */ - int maxres = 0; - int maxprec = 0; - p = tileno % cp->tw; - q = tileno / cp->tw; - - pi[pino].tx0 = int_max(cp->tx0 + p * cp->tdx, image->x0); - pi[pino].ty0 = int_max(cp->ty0 + q * cp->tdy, image->y0); - pi[pino].tx1 = int_min(cp->tx0 + (p + 1) * cp->tdx, image->x1); - pi[pino].ty1 = int_min(cp->ty0 + (q + 1) * cp->tdy, image->y1); - pi[pino].numcomps = image->numcomps; - - pi[pino].comps = (opj_pi_comp_t*) opj_calloc(image->numcomps, - sizeof(opj_pi_comp_t)); - if (!pi[pino].comps) { - /* TODO: throw an error */ - pi_destroy(pi, cp, tileno); - return NULL; - } - - for (compno = 0; compno < pi->numcomps; compno++) { - int tcx0, tcy0, tcx1, tcy1; - opj_pi_comp_t *comp = &pi[pino].comps[compno]; - tccp = &tcp->tccps[compno]; - comp->dx = image->comps[compno].dx; - comp->dy = image->comps[compno].dy; - comp->numresolutions = tccp->numresolutions; - - comp->resolutions = (opj_pi_resolution_t*) opj_calloc(comp->numresolutions, - sizeof(opj_pi_resolution_t)); - if (!comp->resolutions) { - /* TODO: throw an error */ - pi_destroy(pi, cp, tileno); - return NULL; - } - - tcx0 = int_ceildiv(pi->tx0, comp->dx); - tcy0 = int_ceildiv(pi->ty0, comp->dy); - tcx1 = int_ceildiv(pi->tx1, comp->dx); - tcy1 = int_ceildiv(pi->ty1, comp->dy); - if (comp->numresolutions > maxres) { - maxres = comp->numresolutions; - } - - for (resno = 0; resno < comp->numresolutions; resno++) { - int levelno; - int rx0, ry0, rx1, ry1; - int px0, py0, px1, py1; - opj_pi_resolution_t *res = &comp->resolutions[resno]; - if (tccp->csty & J2K_CCP_CSTY_PRT) { - res->pdx = tccp->prcw[resno]; - res->pdy = tccp->prch[resno]; - } else { - res->pdx = 15; - res->pdy = 15; - } - levelno = comp->numresolutions - 1 - resno; - rx0 = int_ceildivpow2(tcx0, levelno); - ry0 = int_ceildivpow2(tcy0, levelno); - rx1 = int_ceildivpow2(tcx1, levelno); - ry1 = int_ceildivpow2(tcy1, levelno); - px0 = int_floordivpow2(rx0, res->pdx) << res->pdx; - py0 = int_floordivpow2(ry0, res->pdy) << res->pdy; - px1 = int_ceildivpow2(rx1, res->pdx) << res->pdx; - py1 = int_ceildivpow2(ry1, res->pdy) << res->pdy; - res->pw = (rx0 == rx1) ? 0 : ((px1 - px0) >> res->pdx); - res->ph = (ry0 == ry1) ? 0 : ((py1 - py0) >> res->pdy); - - if (res->pw * res->ph > maxprec) { - maxprec = res->pw * res->ph; - } - - } - } - - tccp = &tcp->tccps[0]; - pi[pino].step_p = 1; - pi[pino].step_c = maxprec * pi[pino].step_p; - pi[pino].step_r = image->numcomps * pi[pino].step_c; - pi[pino].step_l = maxres * pi[pino].step_r; - - if (pino == 0) { - pi[pino].include = (short int*) opj_calloc(image->numcomps * maxres * - tcp->numlayers * maxprec, sizeof(short int)); - if (!pi[pino].include) { - /* TODO: throw an error */ - pi_destroy(pi, cp, tileno); - return NULL; - } - } else { - pi[pino].include = pi[pino - 1].include; - } - - if (tcp->POC == 0) { - pi[pino].first = 1; - pi[pino].poc.resno0 = 0; - pi[pino].poc.compno0 = 0; - pi[pino].poc.layno1 = tcp->numlayers; - pi[pino].poc.resno1 = maxres; - pi[pino].poc.compno1 = image->numcomps; - pi[pino].poc.prg = tcp->prg; - } else { - pi[pino].first = 1; - pi[pino].poc.resno0 = tcp->pocs[pino].resno0; - pi[pino].poc.compno0 = tcp->pocs[pino].compno0; - pi[pino].poc.layno1 = tcp->pocs[pino].layno1; - pi[pino].poc.resno1 = tcp->pocs[pino].resno1; - pi[pino].poc.compno1 = tcp->pocs[pino].compno1; - pi[pino].poc.prg = tcp->pocs[pino].prg; - } - pi[pino].poc.layno0 = 0; - pi[pino].poc.precno0 = 0; - pi[pino].poc.precno1 = maxprec; - - } - - return pi; -} - - -opj_pi_iterator_t *pi_initialise_encode(opj_image_t *image, opj_cp_t *cp, - int tileno, J2K_T2_MODE t2_mode) -{ - int p, q, pino; - int compno, resno; - int maxres = 0; - int maxprec = 0; - opj_pi_iterator_t *pi = NULL; - opj_tcp_t *tcp = NULL; - opj_tccp_t *tccp = NULL; - - tcp = &cp->tcps[tileno]; - - pi = (opj_pi_iterator_t*) opj_calloc((tcp->numpocs + 1), - sizeof(opj_pi_iterator_t)); - if (!pi) { - return NULL; - } - pi->tp_on = cp->tp_on; - - for (pino = 0; pino < tcp->numpocs + 1; pino++) { - p = tileno % cp->tw; - q = tileno / cp->tw; - - pi[pino].tx0 = int_max(cp->tx0 + p * cp->tdx, image->x0); - pi[pino].ty0 = int_max(cp->ty0 + q * cp->tdy, image->y0); - pi[pino].tx1 = int_min(cp->tx0 + (p + 1) * cp->tdx, image->x1); - pi[pino].ty1 = int_min(cp->ty0 + (q + 1) * cp->tdy, image->y1); - pi[pino].numcomps = image->numcomps; - - pi[pino].comps = (opj_pi_comp_t*) opj_calloc(image->numcomps, - sizeof(opj_pi_comp_t)); - if (!pi[pino].comps) { - pi_destroy(pi, cp, tileno); - return NULL; - } - - for (compno = 0; compno < pi[pino].numcomps; compno++) { - int tcx0, tcy0, tcx1, tcy1; - opj_pi_comp_t *comp = &pi[pino].comps[compno]; - tccp = &tcp->tccps[compno]; - comp->dx = image->comps[compno].dx; - comp->dy = image->comps[compno].dy; - comp->numresolutions = tccp->numresolutions; - - comp->resolutions = (opj_pi_resolution_t*) opj_malloc(comp->numresolutions * - sizeof(opj_pi_resolution_t)); - if (!comp->resolutions) { - pi_destroy(pi, cp, tileno); - return NULL; - } - - tcx0 = int_ceildiv(pi[pino].tx0, comp->dx); - tcy0 = int_ceildiv(pi[pino].ty0, comp->dy); - tcx1 = int_ceildiv(pi[pino].tx1, comp->dx); - tcy1 = int_ceildiv(pi[pino].ty1, comp->dy); - if (comp->numresolutions > maxres) { - maxres = comp->numresolutions; - } - - for (resno = 0; resno < comp->numresolutions; resno++) { - int levelno; - int rx0, ry0, rx1, ry1; - int px0, py0, px1, py1; - opj_pi_resolution_t *res = &comp->resolutions[resno]; - if (tccp->csty & J2K_CCP_CSTY_PRT) { - res->pdx = tccp->prcw[resno]; - res->pdy = tccp->prch[resno]; - } else { - res->pdx = 15; - res->pdy = 15; - } - levelno = comp->numresolutions - 1 - resno; - rx0 = int_ceildivpow2(tcx0, levelno); - ry0 = int_ceildivpow2(tcy0, levelno); - rx1 = int_ceildivpow2(tcx1, levelno); - ry1 = int_ceildivpow2(tcy1, levelno); - px0 = int_floordivpow2(rx0, res->pdx) << res->pdx; - py0 = int_floordivpow2(ry0, res->pdy) << res->pdy; - px1 = int_ceildivpow2(rx1, res->pdx) << res->pdx; - py1 = int_ceildivpow2(ry1, res->pdy) << res->pdy; - res->pw = (rx0 == rx1) ? 0 : ((px1 - px0) >> res->pdx); - res->ph = (ry0 == ry1) ? 0 : ((py1 - py0) >> res->pdy); - - if (res->pw * res->ph > maxprec) { - maxprec = res->pw * res->ph; - } - } - } - - tccp = &tcp->tccps[0]; - pi[pino].step_p = 1; - pi[pino].step_c = maxprec * pi[pino].step_p; - pi[pino].step_r = image->numcomps * pi[pino].step_c; - pi[pino].step_l = maxres * pi[pino].step_r; - - for (compno = 0; compno < pi->numcomps; compno++) { - opj_pi_comp_t *comp = &pi->comps[compno]; - for (resno = 0; resno < comp->numresolutions; resno++) { - int dx, dy; - opj_pi_resolution_t *res = &comp->resolutions[resno]; - dx = comp->dx * (1 << (res->pdx + comp->numresolutions - 1 - resno)); - dy = comp->dy * (1 << (res->pdy + comp->numresolutions - 1 - resno)); - pi[pino].dx = !pi->dx ? dx : int_min(pi->dx, dx); - pi[pino].dy = !pi->dy ? dy : int_min(pi->dy, dy); - } - } - - if (pino == 0) { - pi[pino].include = (short int*) opj_calloc(tcp->numlayers * pi[pino].step_l, - sizeof(short int)); - if (!pi[pino].include) { - pi_destroy(pi, cp, tileno); - return NULL; - } - } else { - pi[pino].include = pi[pino - 1].include; - } - - /* Generation of boundaries for each prog flag*/ - if (tcp->POC && (cp->cinema || ((!cp->cinema) && (t2_mode == FINAL_PASS)))) { - tcp->pocs[pino].compS = tcp->pocs[pino].compno0; - tcp->pocs[pino].compE = tcp->pocs[pino].compno1; - tcp->pocs[pino].resS = tcp->pocs[pino].resno0; - tcp->pocs[pino].resE = tcp->pocs[pino].resno1; - tcp->pocs[pino].layE = tcp->pocs[pino].layno1; - tcp->pocs[pino].prg = tcp->pocs[pino].prg1; - if (pino > 0) { - tcp->pocs[pino].layS = (tcp->pocs[pino].layE > tcp->pocs[pino - 1].layE) ? - tcp->pocs[pino - 1].layE : 0; - } - } else { - tcp->pocs[pino].compS = 0; - tcp->pocs[pino].compE = image->numcomps; - tcp->pocs[pino].resS = 0; - tcp->pocs[pino].resE = maxres; - tcp->pocs[pino].layS = 0; - tcp->pocs[pino].layE = tcp->numlayers; - tcp->pocs[pino].prg = tcp->prg; - } - tcp->pocs[pino].prcS = 0; - tcp->pocs[pino].prcE = maxprec;; - tcp->pocs[pino].txS = pi[pino].tx0; - tcp->pocs[pino].txE = pi[pino].tx1; - tcp->pocs[pino].tyS = pi[pino].ty0; - tcp->pocs[pino].tyE = pi[pino].ty1; - tcp->pocs[pino].dx = pi[pino].dx; - tcp->pocs[pino].dy = pi[pino].dy; - } - return pi; -} - - - -void pi_destroy(opj_pi_iterator_t *pi, opj_cp_t *cp, int tileno) -{ - int compno, pino; - opj_tcp_t *tcp = &cp->tcps[tileno]; - if (pi) { - for (pino = 0; pino < tcp->numpocs + 1; pino++) { - if (pi[pino].comps) { - for (compno = 0; compno < pi->numcomps; compno++) { - opj_pi_comp_t *comp = &pi[pino].comps[compno]; - if (comp->resolutions) { - opj_free(comp->resolutions); - } - } - opj_free(pi[pino].comps); - } - } - if (pi->include) { - opj_free(pi->include); - } - opj_free(pi); - } -} - -opj_bool pi_next(opj_pi_iterator_t * pi) -{ - switch (pi->poc.prg) { - case LRCP: - return pi_next_lrcp(pi); - case RLCP: - return pi_next_rlcp(pi); - case RPCL: - return pi_next_rpcl(pi); - case PCRL: - return pi_next_pcrl(pi); - case CPRL: - return pi_next_cprl(pi); - case PROG_UNKNOWN: - return OPJ_FALSE; - } - - return OPJ_FALSE; -} - -opj_bool pi_create_encode(opj_pi_iterator_t *pi, opj_cp_t *cp, int tileno, - int pino, int tpnum, int tppos, J2K_T2_MODE t2_mode, int cur_totnum_tp) -{ - char prog[4]; - int i; - int incr_top = 1, resetX = 0; - opj_tcp_t *tcps = &cp->tcps[tileno]; - opj_poc_t *tcp = &tcps->pocs[pino]; - - pi[pino].first = 1; - pi[pino].poc.prg = tcp->prg; - - switch (tcp->prg) { - case CPRL: - strncpy(prog, "CPRL", 4); - break; - case LRCP: - strncpy(prog, "LRCP", 4); - break; - case PCRL: - strncpy(prog, "PCRL", 4); - break; - case RLCP: - strncpy(prog, "RLCP", 4); - break; - case RPCL: - strncpy(prog, "RPCL", 4); - break; - case PROG_UNKNOWN: - return OPJ_TRUE; - } - - if (!(cp->tp_on && ((!cp->cinema && (t2_mode == FINAL_PASS)) || cp->cinema))) { - pi[pino].poc.resno0 = tcp->resS; - pi[pino].poc.resno1 = tcp->resE; - pi[pino].poc.compno0 = tcp->compS; - pi[pino].poc.compno1 = tcp->compE; - pi[pino].poc.layno0 = tcp->layS; - pi[pino].poc.layno1 = tcp->layE; - pi[pino].poc.precno0 = tcp->prcS; - pi[pino].poc.precno1 = tcp->prcE; - pi[pino].poc.tx0 = tcp->txS; - pi[pino].poc.ty0 = tcp->tyS; - pi[pino].poc.tx1 = tcp->txE; - pi[pino].poc.ty1 = tcp->tyE; - } else { - if (tpnum < cur_totnum_tp) { - for (i = 3; i >= 0; i--) { - switch (prog[i]) { - case 'C': - if (i > tppos) { - pi[pino].poc.compno0 = tcp->compS; - pi[pino].poc.compno1 = tcp->compE; - } else { - if (tpnum == 0) { - tcp->comp_t = tcp->compS; - pi[pino].poc.compno0 = tcp->comp_t; - pi[pino].poc.compno1 = tcp->comp_t + 1; - tcp->comp_t += 1; - } else { - if (incr_top == 1) { - if (tcp->comp_t == tcp->compE) { - tcp->comp_t = tcp->compS; - pi[pino].poc.compno0 = tcp->comp_t; - pi[pino].poc.compno1 = tcp->comp_t + 1; - tcp->comp_t += 1; - incr_top = 1; - } else { - pi[pino].poc.compno0 = tcp->comp_t; - pi[pino].poc.compno1 = tcp->comp_t + 1; - tcp->comp_t += 1; - incr_top = 0; - } - } else { - pi[pino].poc.compno0 = tcp->comp_t - 1; - pi[pino].poc.compno1 = tcp->comp_t; - } - } - } - break; - - case 'R': - if (i > tppos) { - pi[pino].poc.resno0 = tcp->resS; - pi[pino].poc.resno1 = tcp->resE; - } else { - if (tpnum == 0) { - tcp->res_t = tcp->resS; - pi[pino].poc.resno0 = tcp->res_t; - pi[pino].poc.resno1 = tcp->res_t + 1; - tcp->res_t += 1; - } else { - if (incr_top == 1) { - if (tcp->res_t == tcp->resE) { - tcp->res_t = tcp->resS; - pi[pino].poc.resno0 = tcp->res_t; - pi[pino].poc.resno1 = tcp->res_t + 1; - tcp->res_t += 1; - incr_top = 1; - } else { - pi[pino].poc.resno0 = tcp->res_t; - pi[pino].poc.resno1 = tcp->res_t + 1; - tcp->res_t += 1; - incr_top = 0; - } - } else { - pi[pino].poc.resno0 = tcp->res_t - 1; - pi[pino].poc.resno1 = tcp->res_t; - } - } - } - break; - - case 'L': - if (i > tppos) { - pi[pino].poc.layno0 = tcp->layS; - pi[pino].poc.layno1 = tcp->layE; - } else { - if (tpnum == 0) { - tcp->lay_t = tcp->layS; - pi[pino].poc.layno0 = tcp->lay_t; - pi[pino].poc.layno1 = tcp->lay_t + 1; - tcp->lay_t += 1; - } else { - if (incr_top == 1) { - if (tcp->lay_t == tcp->layE) { - tcp->lay_t = tcp->layS; - pi[pino].poc.layno0 = tcp->lay_t; - pi[pino].poc.layno1 = tcp->lay_t + 1; - tcp->lay_t += 1; - incr_top = 1; - } else { - pi[pino].poc.layno0 = tcp->lay_t; - pi[pino].poc.layno1 = tcp->lay_t + 1; - tcp->lay_t += 1; - incr_top = 0; - } - } else { - pi[pino].poc.layno0 = tcp->lay_t - 1; - pi[pino].poc.layno1 = tcp->lay_t; - } - } - } - break; - - case 'P': - switch (tcp->prg) { - case LRCP: - case RLCP: - if (i > tppos) { - pi[pino].poc.precno0 = tcp->prcS; - pi[pino].poc.precno1 = tcp->prcE; - } else { - if (tpnum == 0) { - tcp->prc_t = tcp->prcS; - pi[pino].poc.precno0 = tcp->prc_t; - pi[pino].poc.precno1 = tcp->prc_t + 1; - tcp->prc_t += 1; - } else { - if (incr_top == 1) { - if (tcp->prc_t == tcp->prcE) { - tcp->prc_t = tcp->prcS; - pi[pino].poc.precno0 = tcp->prc_t; - pi[pino].poc.precno1 = tcp->prc_t + 1; - tcp->prc_t += 1; - incr_top = 1; - } else { - pi[pino].poc.precno0 = tcp->prc_t; - pi[pino].poc.precno1 = tcp->prc_t + 1; - tcp->prc_t += 1; - incr_top = 0; - } - } else { - pi[pino].poc.precno0 = tcp->prc_t - 1; - pi[pino].poc.precno1 = tcp->prc_t; - } - } - } - break; - default: - if (i > tppos) { - pi[pino].poc.tx0 = tcp->txS; - pi[pino].poc.ty0 = tcp->tyS; - pi[pino].poc.tx1 = tcp->txE; - pi[pino].poc.ty1 = tcp->tyE; - } else { - if (tpnum == 0) { - tcp->tx0_t = tcp->txS; - tcp->ty0_t = tcp->tyS; - pi[pino].poc.tx0 = tcp->tx0_t; - pi[pino].poc.tx1 = tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx); - pi[pino].poc.ty0 = tcp->ty0_t; - pi[pino].poc.ty1 = tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy); - tcp->tx0_t = pi[pino].poc.tx1; - tcp->ty0_t = pi[pino].poc.ty1; - } else { - if (incr_top == 1) { - if (tcp->tx0_t >= tcp->txE) { - if (tcp->ty0_t >= tcp->tyE) { - tcp->ty0_t = tcp->tyS; - pi[pino].poc.ty0 = tcp->ty0_t; - pi[pino].poc.ty1 = tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy); - tcp->ty0_t = pi[pino].poc.ty1; - incr_top = 1; - resetX = 1; - } else { - pi[pino].poc.ty0 = tcp->ty0_t; - pi[pino].poc.ty1 = tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy); - tcp->ty0_t = pi[pino].poc.ty1; - incr_top = 0; - resetX = 1; - } - if (resetX == 1) { - tcp->tx0_t = tcp->txS; - pi[pino].poc.tx0 = tcp->tx0_t; - pi[pino].poc.tx1 = tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx); - tcp->tx0_t = pi[pino].poc.tx1; - } - } else { - pi[pino].poc.tx0 = tcp->tx0_t; - pi[pino].poc.tx1 = tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx); - tcp->tx0_t = pi[pino].poc.tx1; - pi[pino].poc.ty0 = tcp->ty0_t - tcp->dy - (tcp->ty0_t % tcp->dy); - pi[pino].poc.ty1 = tcp->ty0_t; - incr_top = 0; - } - } else { - pi[pino].poc.tx0 = tcp->tx0_t - tcp->dx - (tcp->tx0_t % tcp->dx); - pi[pino].poc.tx1 = tcp->tx0_t; - pi[pino].poc.ty0 = tcp->ty0_t - tcp->dy - (tcp->ty0_t % tcp->dy); - pi[pino].poc.ty1 = tcp->ty0_t; - } - } - } - break; - } - break; - } - } - } - } - return OPJ_FALSE; -} - diff --git a/test/bug-hunting/cve/CVE-2019-10018/Function.cc b/test/bug-hunting/cve/CVE-2019-10018/Function.cc deleted file mode 100644 index 72cadd9bed1..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10018/Function.cc +++ /dev/null @@ -1,1567 +0,0 @@ -//======================================================================== -// -// Function.cc -// -// Copyright 2001-2003 Glyph & Cog, LLC -// -//======================================================================== - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include -#include -#include -#include -#include "gmem.h" -#include "gmempp.h" -#include "GList.h" -#include "Object.h" -#include "Dict.h" -#include "Stream.h" -#include "Error.h" -#include "Function.h" - -//------------------------------------------------------------------------ - -// Max depth of nested functions. This is used to catch infinite -// loops in the function object structure. -#define recursionLimit 8 - -//------------------------------------------------------------------------ -// Function -//------------------------------------------------------------------------ - -Function::Function() { -} - -Function::~Function() { -} - -Function *Function::parse(Object *funcObj, int recursion) { - Function *func; - Dict *dict; - int funcType; - Object obj1; - - if (recursion > recursionLimit) { - error(errSyntaxError, -1, "Loop detected in function objects"); - return NULL; - } - - if (funcObj->isStream()) { - dict = funcObj->streamGetDict(); - } else if (funcObj->isDict()) { - dict = funcObj->getDict(); - } else if (funcObj->isName("Identity")) { - return new IdentityFunction(); - } else { - error(errSyntaxError, -1, "Expected function dictionary or stream"); - return NULL; - } - - if (!dict->lookup("FunctionType", &obj1)->isInt()) { - error(errSyntaxError, -1, "Function type is missing or wrong type"); - obj1.free(); - return NULL; - } - funcType = obj1.getInt(); - obj1.free(); - - if (funcType == 0) { - func = new SampledFunction(funcObj, dict); - } else if (funcType == 2) { - func = new ExponentialFunction(funcObj, dict); - } else if (funcType == 3) { - func = new StitchingFunction(funcObj, dict, recursion); - } else if (funcType == 4) { - func = new PostScriptFunction(funcObj, dict); - } else { - error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType); - return NULL; - } - if (!func->isOk()) { - delete func; - return NULL; - } - - return func; -} - -GBool Function::init(Dict *dict) { - Object obj1, obj2; - int i; - - //----- Domain - if (!dict->lookup("Domain", &obj1)->isArray()) { - error(errSyntaxError, -1, "Function is missing domain"); - goto err2; - } - m = obj1.arrayGetLength() / 2; - if (m > funcMaxInputs) { - error(errSyntaxError, -1, - "Functions with more than {0:d} inputs are unsupported", - funcMaxInputs); - goto err2; - } - for (i = 0; i < m; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function domain array"); - goto err1; - } - domain[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function domain array"); - goto err1; - } - domain[i][1] = obj2.getNum(); - obj2.free(); - } - obj1.free(); - - //----- Range - hasRange = gFalse; - n = 0; - if (dict->lookup("Range", &obj1)->isArray()) { - hasRange = gTrue; - n = obj1.arrayGetLength() / 2; - if (n > funcMaxOutputs) { - error(errSyntaxError, -1, - "Functions with more than {0:d} outputs are unsupported", - funcMaxOutputs); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function range array"); - goto err1; - } - range[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function range array"); - goto err1; - } - range[i][1] = obj2.getNum(); - obj2.free(); - } - } - obj1.free(); - - return gTrue; - - err1: - obj2.free(); - err2: - obj1.free(); - return gFalse; -} - -//------------------------------------------------------------------------ -// IdentityFunction -//------------------------------------------------------------------------ - -IdentityFunction::IdentityFunction() { - int i; - - // fill these in with arbitrary values just in case they get used - // somewhere - m = funcMaxInputs; - n = funcMaxOutputs; - for (i = 0; i < funcMaxInputs; ++i) { - domain[i][0] = 0; - domain[i][1] = 1; - } - hasRange = gFalse; -} - -IdentityFunction::~IdentityFunction() { -} - -void IdentityFunction::transform(double *in, double *out) { - int i; - - for (i = 0; i < funcMaxOutputs; ++i) { - out[i] = in[i]; - } -} - -//------------------------------------------------------------------------ -// SampledFunction -//------------------------------------------------------------------------ - -SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { - Stream *str; - int sampleBits; - double sampleMul; - Object obj1, obj2; - Guint buf, bitMask; - int bits; - Guint s; - double in[funcMaxInputs]; - int i, j, t, bit, idx; - - idxOffset = NULL; - samples = NULL; - sBuf = NULL; - ok = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (!hasRange) { - error(errSyntaxError, -1, "Type 0 function is missing range"); - goto err1; - } - if (m > sampledFuncMaxInputs) { - error(errSyntaxError, -1, - "Sampled functions with more than {0:d} inputs are unsupported", - sampledFuncMaxInputs); - goto err1; - } - - //----- buffer - sBuf = (double *)gmallocn(1 << m, sizeof(double)); - - //----- get the stream - if (!funcObj->isStream()) { - error(errSyntaxError, -1, "Type 0 function isn't a stream"); - goto err1; - } - str = funcObj->getStream(); - - //----- Size - if (!dict->lookup("Size", &obj1)->isArray() || - obj1.arrayGetLength() != m) { - error(errSyntaxError, -1, "Function has missing or invalid size array"); - goto err2; - } - for (i = 0; i < m; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isInt()) { - error(errSyntaxError, -1, "Illegal value in function size array"); - goto err3; - } - sampleSize[i] = obj2.getInt(); - if (sampleSize[i] <= 0) { - error(errSyntaxError, -1, "Illegal non-positive value in function size array"); - goto err3; - } - obj2.free(); - } - obj1.free(); - idxOffset = (int *)gmallocn(1 << m, sizeof(int)); - for (i = 0; i < (1<= 1; --j, t <<= 1) { - if (sampleSize[j] == 1) { - bit = 0; - } else { - bit = (t >> (m - 1)) & 1; - } - idx = (idx + bit) * sampleSize[j-1]; - } - if (sampleSize[0] == 1) { - bit = 0; - } else { - bit = (t >> (m - 1)) & 1; - } - idxOffset[i] = (idx + bit) * n; - } - - //----- BitsPerSample - if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { - error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample"); - goto err2; - } - sampleBits = obj1.getInt(); - sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1); - obj1.free(); - - //----- Encode - if (dict->lookup("Encode", &obj1)->isArray() && - obj1.arrayGetLength() == 2*m) { - for (i = 0; i < m; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function encode array"); - goto err3; - } - encode[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function encode array"); - goto err3; - } - encode[i][1] = obj2.getNum(); - obj2.free(); - } - } else { - for (i = 0; i < m; ++i) { - encode[i][0] = 0; - encode[i][1] = sampleSize[i] - 1; - } - } - obj1.free(); - for (i = 0; i < m; ++i) { - inputMul[i] = (encode[i][1] - encode[i][0]) / - (domain[i][1] - domain[i][0]); - } - - //----- Decode - if (dict->lookup("Decode", &obj1)->isArray() && - obj1.arrayGetLength() == 2*n) { - for (i = 0; i < n; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function decode array"); - goto err3; - } - decode[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function decode array"); - goto err3; - } - decode[i][1] = obj2.getNum(); - obj2.free(); - } - } else { - for (i = 0; i < n; ++i) { - decode[i][0] = range[i][0]; - decode[i][1] = range[i][1]; - } - } - obj1.free(); - - //----- samples - nSamples = n; - for (i = 0; i < m; ++i) - nSamples *= sampleSize[i]; - samples = (double *)gmallocn(nSamples, sizeof(double)); - buf = 0; - bits = 0; - bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU; - str->reset(); - for (i = 0; i < nSamples; ++i) { - if (sampleBits == 8) { - s = str->getChar(); - } else if (sampleBits == 16) { - s = str->getChar(); - s = (s << 8) + str->getChar(); - } else if (sampleBits == 32) { - s = str->getChar(); - s = (s << 8) + str->getChar(); - s = (s << 8) + str->getChar(); - s = (s << 8) + str->getChar(); - } else { - while (bits < sampleBits) { - buf = (buf << 8) | (str->getChar() & 0xff); - bits += 8; - } - s = (buf >> (bits - sampleBits)) & bitMask; - bits -= sampleBits; - } - samples[i] = (double)s * sampleMul; - } - str->close(); - - // set up the cache - for (i = 0; i < m; ++i) { - in[i] = domain[i][0]; - cacheIn[i] = in[i] - 1; - } - transform(in, cacheOut); - - ok = gTrue; - return; - - err3: - obj2.free(); - err2: - obj1.free(); - err1: - return; -} - -SampledFunction::~SampledFunction() { - if (idxOffset) { - gfree(idxOffset); - } - if (samples) { - gfree(samples); - } - if (sBuf) { - gfree(sBuf); - } -} - -SampledFunction::SampledFunction(SampledFunction *func) { - memcpy((void *)this, (void *)func, sizeof(SampledFunction)); - idxOffset = (int *)gmallocn(1 << m, sizeof(int)); - memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int)); - samples = (double *)gmallocn(nSamples, sizeof(double)); - memcpy(samples, func->samples, nSamples * sizeof(double)); - sBuf = (double *)gmallocn(1 << m, sizeof(double)); -} - -void SampledFunction::transform(double *in, double *out) { - double x; - int e[funcMaxInputs]; - double efrac0[funcMaxInputs]; - double efrac1[funcMaxInputs]; - int i, j, k, idx0, t; - - // check the cache - for (i = 0; i < m; ++i) { - if (in[i] != cacheIn[i]) { - break; - } - } - if (i == m) { - for (i = 0; i < n; ++i) { - out[i] = cacheOut[i]; - } - return; - } - - // map input values into sample array - for (i = 0; i < m; ++i) { - x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0]; - if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x) - x = 0; - } else if (x > sampleSize[i] - 1) { - x = sampleSize[i] - 1; - } - e[i] = (int)x; - if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) { - // this happens if in[i] = domain[i][1] - e[i] = sampleSize[i] - 2; - } - efrac1[i] = x - e[i]; - efrac0[i] = 1 - efrac1[i]; - } - - // compute index for the first sample to be used - idx0 = 0; - for (k = m - 1; k >= 1; --k) { - idx0 = (idx0 + e[k]) * sampleSize[k-1]; - } - idx0 = (idx0 + e[0]) * n; - - // for each output, do m-linear interpolation - for (i = 0; i < n; ++i) { - - // pull 2^m values out of the sample array - for (j = 0; j < (1<>= 1) { - for (k = 0; k < t; k += 2) { - sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1]; - } - } - - // map output value to range - out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]; - if (out[i] < range[i][0]) { - out[i] = range[i][0]; - } else if (out[i] > range[i][1]) { - out[i] = range[i][1]; - } - } - - // save current result in the cache - for (i = 0; i < m; ++i) { - cacheIn[i] = in[i]; - } - for (i = 0; i < n; ++i) { - cacheOut[i] = out[i]; - } -} - -//------------------------------------------------------------------------ -// ExponentialFunction -//------------------------------------------------------------------------ - -ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { - Object obj1, obj2; - int i; - - ok = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (m != 1) { - error(errSyntaxError, -1, "Exponential function with more than one input"); - goto err1; - } - - //----- C0 - if (dict->lookup("C0", &obj1)->isArray()) { - if (hasRange && obj1.arrayGetLength() != n) { - error(errSyntaxError, -1, "Function's C0 array is wrong length"); - goto err2; - } - n = obj1.arrayGetLength(); - if (n > funcMaxOutputs) { - error(errSyntaxError, -1, - "Functions with more than {0:d} outputs are unsupported", - funcMaxOutputs); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function C0 array"); - goto err3; - } - c0[i] = obj2.getNum(); - obj2.free(); - } - } else { - if (hasRange && n != 1) { - error(errSyntaxError, -1, "Function's C0 array is wrong length"); - goto err2; - } - n = 1; - c0[0] = 0; - } - obj1.free(); - - //----- C1 - if (dict->lookup("C1", &obj1)->isArray()) { - if (obj1.arrayGetLength() != n) { - error(errSyntaxError, -1, "Function's C1 array is wrong length"); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function C1 array"); - goto err3; - } - c1[i] = obj2.getNum(); - obj2.free(); - } - } else { - if (n != 1) { - error(errSyntaxError, -1, "Function's C1 array is wrong length"); - goto err2; - } - c1[0] = 1; - } - obj1.free(); - - //----- N (exponent) - if (!dict->lookup("N", &obj1)->isNum()) { - error(errSyntaxError, -1, "Function has missing or invalid N"); - goto err2; - } - e = obj1.getNum(); - obj1.free(); - - ok = gTrue; - return; - - err3: - obj2.free(); - err2: - obj1.free(); - err1: - return; -} - -ExponentialFunction::~ExponentialFunction() { -} - -ExponentialFunction::ExponentialFunction(ExponentialFunction *func) { - memcpy((void *)this, (void *)func, sizeof(ExponentialFunction)); -} - -void ExponentialFunction::transform(double *in, double *out) { - double x; - int i; - - if (in[0] < domain[0][0]) { - x = domain[0][0]; - } else if (in[0] > domain[0][1]) { - x = domain[0][1]; - } else { - x = in[0]; - } - for (i = 0; i < n; ++i) { - out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]); - if (hasRange) { - if (out[i] < range[i][0]) { - out[i] = range[i][0]; - } else if (out[i] > range[i][1]) { - out[i] = range[i][1]; - } - } - } - return; -} - -//------------------------------------------------------------------------ -// StitchingFunction -//------------------------------------------------------------------------ - -StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, - int recursion) { - Object obj1, obj2; - int i; - - ok = gFalse; - funcs = NULL; - bounds = NULL; - encode = NULL; - scale = NULL; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (m != 1) { - error(errSyntaxError, -1, "Stitching function with more than one input"); - goto err1; - } - - //----- Functions - if (!dict->lookup("Functions", &obj1)->isArray()) { - error(errSyntaxError, -1, - "Missing 'Functions' entry in stitching function"); - goto err1; - } - k = obj1.arrayGetLength(); - funcs = (Function **)gmallocn(k, sizeof(Function *)); - bounds = (double *)gmallocn(k + 1, sizeof(double)); - encode = (double *)gmallocn(2 * k, sizeof(double)); - scale = (double *)gmallocn(k, sizeof(double)); - for (i = 0; i < k; ++i) { - funcs[i] = NULL; - } - for (i = 0; i < k; ++i) { - if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2), - recursion + 1))) { - goto err2; - } - if (funcs[i]->getInputSize() != 1 || - (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { - error(errSyntaxError, -1, - "Incompatible subfunctions in stitching function"); - goto err2; - } - obj2.free(); - } - obj1.free(); - - //----- Bounds - if (!dict->lookup("Bounds", &obj1)->isArray() || - obj1.arrayGetLength() != k - 1) { - error(errSyntaxError, -1, - "Missing or invalid 'Bounds' entry in stitching function"); - goto err1; - } - bounds[0] = domain[0][0]; - for (i = 1; i < k; ++i) { - if (!obj1.arrayGet(i - 1, &obj2)->isNum()) { - error(errSyntaxError, -1, - "Invalid type in 'Bounds' array in stitching function"); - goto err2; - } - bounds[i] = obj2.getNum(); - obj2.free(); - } - bounds[k] = domain[0][1]; - obj1.free(); - - //----- Encode - if (!dict->lookup("Encode", &obj1)->isArray() || - obj1.arrayGetLength() != 2 * k) { - error(errSyntaxError, -1, - "Missing or invalid 'Encode' entry in stitching function"); - goto err1; - } - for (i = 0; i < 2 * k; ++i) { - if (!obj1.arrayGet(i, &obj2)->isNum()) { - error(errSyntaxError, -1, - "Invalid type in 'Encode' array in stitching function"); - goto err2; - } - encode[i] = obj2.getNum(); - obj2.free(); - } - obj1.free(); - - //----- pre-compute the scale factors - for (i = 0; i < k; ++i) { - if (bounds[i] == bounds[i+1]) { - // avoid a divide-by-zero -- in this situation, function i will - // never be used anyway - scale[i] = 0; - } else { - scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]); - } - } - - ok = gTrue; - return; - - err2: - obj2.free(); - err1: - obj1.free(); -} - -StitchingFunction::StitchingFunction(StitchingFunction *func) { - int i; - - memcpy((void *)this, (void *)func, sizeof(StitchingFunction)); - funcs = (Function **)gmallocn(k, sizeof(Function *)); - for (i = 0; i < k; ++i) { - funcs[i] = func->funcs[i]->copy(); - } - bounds = (double *)gmallocn(k + 1, sizeof(double)); - memcpy(bounds, func->bounds, (k + 1) * sizeof(double)); - encode = (double *)gmallocn(2 * k, sizeof(double)); - memcpy(encode, func->encode, 2 * k * sizeof(double)); - scale = (double *)gmallocn(k, sizeof(double)); - memcpy(scale, func->scale, k * sizeof(double)); - ok = gTrue; -} - -StitchingFunction::~StitchingFunction() { - int i; - - if (funcs) { - for (i = 0; i < k; ++i) { - if (funcs[i]) { - delete funcs[i]; - } - } - } - gfree(funcs); - gfree(bounds); - gfree(encode); - gfree(scale); -} - -void StitchingFunction::transform(double *in, double *out) { - double x; - int i; - - if (in[0] < domain[0][0]) { - x = domain[0][0]; - } else if (in[0] > domain[0][1]) { - x = domain[0][1]; - } else { - x = in[0]; - } - for (i = 0; i < k - 1; ++i) { - if (x < bounds[i+1]) { - break; - } - } - x = encode[2*i] + (x - bounds[i]) * scale[i]; - funcs[i]->transform(&x, out); -} - -//------------------------------------------------------------------------ -// PostScriptFunction -//------------------------------------------------------------------------ - -// This is not an enum, because we can't foreward-declare the enum -// type in Function.h -// -// NB: This must be kept in sync with psOpNames[] below. -#define psOpAbs 0 -#define psOpAdd 1 -#define psOpAnd 2 -#define psOpAtan 3 -#define psOpBitshift 4 -#define psOpCeiling 5 -#define psOpCopy 6 -#define psOpCos 7 -#define psOpCvi 8 -#define psOpCvr 9 -#define psOpDiv 10 -#define psOpDup 11 -#define psOpEq 12 -#define psOpExch 13 -#define psOpExp 14 -#define psOpFalse 15 -#define psOpFloor 16 -#define psOpGe 17 -#define psOpGt 18 -#define psOpIdiv 19 -#define psOpIndex 20 -#define psOpLe 21 -#define psOpLn 22 -#define psOpLog 23 -#define psOpLt 24 -#define psOpMod 25 -#define psOpMul 26 -#define psOpNe 27 -#define psOpNeg 28 -#define psOpNot 29 -#define psOpOr 30 -#define psOpPop 31 -#define psOpRoll 32 -#define psOpRound 33 -#define psOpSin 34 -#define psOpSqrt 35 -#define psOpSub 36 -#define psOpTrue 37 -#define psOpTruncate 38 -#define psOpXor 39 -// the push/j/jz ops are used internally (and are not listed in psOpNames[]) -#define psOpPush 40 -#define psOpJ 41 -#define psOpJz 42 - -#define nPSOps (sizeof(psOpNames) / sizeof(const char *)) - -// Note: 'if' and 'ifelse' are parsed separately. -// The rest are listed here in alphabetical order. -// -// NB: This must be kept in sync with the psOpXXX defines above. -static const char *psOpNames[] = { - "abs", - "add", - "and", - "atan", - "bitshift", - "ceiling", - "copy", - "cos", - "cvi", - "cvr", - "div", - "dup", - "eq", - "exch", - "exp", - "false", - "floor", - "ge", - "gt", - "idiv", - "index", - "le", - "ln", - "log", - "lt", - "mod", - "mul", - "ne", - "neg", - "not", - "or", - "pop", - "roll", - "round", - "sin", - "sqrt", - "sub", - "true", - "truncate", - "xor" -}; - -struct PSCode { - int op; - union { - double d; - int i; - } val; -}; - -#define psStackSize 100 - -PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { - Stream *str; - GList *tokens; - GString *tok; - double in[funcMaxInputs]; - int tokPtr, codePtr, i; - - codeString = NULL; - code = NULL; - codeSize = 0; - ok = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (!hasRange) { - error(errSyntaxError, -1, "Type 4 function is missing range"); - goto err1; - } - - //----- get the stream - if (!funcObj->isStream()) { - error(errSyntaxError, -1, "Type 4 function isn't a stream"); - goto err1; - } - str = funcObj->getStream(); - - //----- tokenize the function - codeString = new GString(); - tokens = new GList(); - str->reset(); - while ((tok = getToken(str))) { - tokens->append(tok); - } - str->close(); - - //----- parse the function - if (tokens->getLength() < 1 || - ((GString *)tokens->get(0))->cmp("{")) { - error(errSyntaxError, -1, "Expected '{{' at start of PostScript function"); - goto err2; - } - tokPtr = 1; - codePtr = 0; - if (!parseCode(tokens, &tokPtr, &codePtr)) { - goto err2; - } - codeLen = codePtr; - - //----- set up the cache - for (i = 0; i < m; ++i) { - in[i] = domain[i][0]; - cacheIn[i] = in[i] - 1; - } - transform(in, cacheOut); - - ok = gTrue; - - err2: - deleteGList(tokens, GString); - err1: - return; -} - -PostScriptFunction::PostScriptFunction(PostScriptFunction *func) { - memcpy((void *)this, (void *)func, sizeof(PostScriptFunction)); - codeString = func->codeString->copy(); - code = (PSCode *)gmallocn(codeSize, sizeof(PSCode)); - memcpy(code, func->code, codeSize * sizeof(PSCode)); -} - -PostScriptFunction::~PostScriptFunction() { - gfree(code); - if (codeString) { - delete codeString; - } -} - -void PostScriptFunction::transform(double *in, double *out) { - double stack[psStackSize]; - double x; - int sp, i; - - // check the cache - for (i = 0; i < m; ++i) { - if (in[i] != cacheIn[i]) { - break; - } - } - if (i == m) { - for (i = 0; i < n; ++i) { - out[i] = cacheOut[i]; - } - return; - } - - for (i = 0; i < m; ++i) { - stack[psStackSize - 1 - i] = in[i]; - } - sp = exec(stack, psStackSize - m); - // if (sp < psStackSize - n) { - // error(errSyntaxWarning, -1, - // "Extra values on stack at end of PostScript function"); - // } - if (sp > psStackSize - n) { - error(errSyntaxError, -1, "Stack underflow in PostScript function"); - sp = psStackSize - n; - } - for (i = 0; i < n; ++i) { - x = stack[sp + n - 1 - i]; - if (x < range[i][0]) { - out[i] = range[i][0]; - } else if (x > range[i][1]) { - out[i] = range[i][1]; - } else { - out[i] = x; - } - } - - // save current result in the cache - for (i = 0; i < m; ++i) { - cacheIn[i] = in[i]; - } - for (i = 0; i < n; ++i) { - cacheOut[i] = out[i]; - } -} - -GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) { - GString *tok; - char *p; - int a, b, mid, cmp; - int codePtr0, codePtr1; - - while (1) { - if (*tokPtr >= tokens->getLength()) { - error(errSyntaxError, -1, - "Unexpected end of PostScript function stream"); - return gFalse; - } - tok = (GString *)tokens->get((*tokPtr)++); - p = tok->getCString(); - if (isdigit(*p) || *p == '.' || *p == '-') { - addCodeD(codePtr, psOpPush, atof(tok->getCString())); - } else if (!tok->cmp("{")) { - codePtr0 = *codePtr; - addCodeI(codePtr, psOpJz, 0); - if (!parseCode(tokens, tokPtr, codePtr)) { - return gFalse; - } - if (*tokPtr >= tokens->getLength()) { - error(errSyntaxError, -1, - "Unexpected end of PostScript function stream"); - return gFalse; - } - tok = (GString *)tokens->get((*tokPtr)++); - if (!tok->cmp("if")) { - code[codePtr0].val.i = *codePtr; - } else if (!tok->cmp("{")) { - codePtr1 = *codePtr; - addCodeI(codePtr, psOpJ, 0); - code[codePtr0].val.i = *codePtr; - if (!parseCode(tokens, tokPtr, codePtr)) { - return gFalse; - } - if (*tokPtr >= tokens->getLength()) { - error(errSyntaxError, -1, - "Unexpected end of PostScript function stream"); - return gFalse; - } - tok = (GString *)tokens->get((*tokPtr)++); - if (!tok->cmp("ifelse")) { - code[codePtr1].val.i = *codePtr; - } else { - error(errSyntaxError, -1, - "Expected 'ifelse' in PostScript function stream"); - return gFalse; - } - } else { - error(errSyntaxError, -1, - "Expected 'if' in PostScript function stream"); - return gFalse; - } - } else if (!tok->cmp("}")) { - break; - } else if (!tok->cmp("if")) { - error(errSyntaxError, -1, - "Unexpected 'if' in PostScript function stream"); - return gFalse; - } else if (!tok->cmp("ifelse")) { - error(errSyntaxError, -1, - "Unexpected 'ifelse' in PostScript function stream"); - return gFalse; - } else { - a = -1; - b = nPSOps; - cmp = 0; // make gcc happy - // invariant: psOpNames[a] < tok < psOpNames[b] - while (b - a > 1) { - mid = (a + b) / 2; - cmp = tok->cmp(psOpNames[mid]); - if (cmp > 0) { - a = mid; - } else if (cmp < 0) { - b = mid; - } else { - a = b = mid; - } - } - if (cmp != 0) { - error(errSyntaxError, -1, - "Unknown operator '{0:t}' in PostScript function", - tok); - return gFalse; - } - addCode(codePtr, a); - } - } - return gTrue; -} - -void PostScriptFunction::addCode(int *codePtr, int op) { - if (*codePtr >= codeSize) { - if (codeSize) { - codeSize *= 2; - } else { - codeSize = 16; - } - code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); - } - code[*codePtr].op = op; - ++(*codePtr); -} - -void PostScriptFunction::addCodeI(int *codePtr, int op, int x) { - if (*codePtr >= codeSize) { - if (codeSize) { - codeSize *= 2; - } else { - codeSize = 16; - } - code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); - } - code[*codePtr].op = op; - code[*codePtr].val.i = x; - ++(*codePtr); -} - -void PostScriptFunction::addCodeD(int *codePtr, int op, double x) { - if (*codePtr >= codeSize) { - if (codeSize) { - codeSize *= 2; - } else { - codeSize = 16; - } - code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); - } - code[*codePtr].op = op; - code[*codePtr].val.d = x; - ++(*codePtr); -} - -GString *PostScriptFunction::getToken(Stream *str) { - GString *s; - int c; - GBool comment; - - s = new GString(); - comment = gFalse; - while (1) { - if ((c = str->getChar()) == EOF) { - delete s; - return NULL; - } - codeString->append((char)c); - if (comment) { - if (c == '\x0a' || c == '\x0d') { - comment = gFalse; - } - } else if (c == '%') { - comment = gTrue; - } else if (!isspace(c)) { - break; - } - } - if (c == '{' || c == '}') { - s->append((char)c); - } else if (isdigit(c) || c == '.' || c == '-') { - while (1) { - s->append((char)c); - c = str->lookChar(); - if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { - break; - } - str->getChar(); - codeString->append((char)c); - } - } else { - while (1) { - s->append((char)c); - c = str->lookChar(); - if (c == EOF || !isalnum(c)) { - break; - } - str->getChar(); - codeString->append((char)c); - } - } - return s; -} - -int PostScriptFunction::exec(double *stack, int sp0) { - PSCode *c; - double tmp[psStackSize]; - double t; - int sp, ip, nn, k, i; - - sp = sp0; - ip = 0; - while (ip < codeLen) { - c = &code[ip++]; - switch(c->op) { - case psOpAbs: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = fabs(stack[sp]); - break; - case psOpAdd: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] + stack[sp]; - ++sp; - break; - case psOpAnd: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp]; - ++sp; - break; - case psOpAtan: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = atan2(stack[sp + 1], stack[sp]); - ++sp; - break; - case psOpBitshift: - if (sp + 1 >= psStackSize) { - goto underflow; - } - k = (int)stack[sp + 1]; - nn = (int)stack[sp]; - if (nn > 0) { - stack[sp + 1] = k << nn; - } else if (nn < 0) { - stack[sp + 1] = k >> -nn; - } else { - stack[sp + 1] = k; - } - ++sp; - break; - case psOpCeiling: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = ceil(stack[sp]); - break; - case psOpCopy: - if (sp >= psStackSize) { - goto underflow; - } - nn = (int)stack[sp++]; - if (nn < 0) { - goto invalidArg; - } - if (sp + nn > psStackSize) { - goto underflow; - } - if (sp - nn < 0) { - goto overflow; - } - for (i = 0; i < nn; ++i) { - stack[sp - nn + i] = stack[sp + i]; - } - sp -= nn; - break; - case psOpCos: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = cos(stack[sp]); - break; - case psOpCvi: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = (int)stack[sp]; - break; - case psOpCvr: - if (sp >= psStackSize) { - goto underflow; - } - break; - case psOpDiv: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] / stack[sp]; - ++sp; - break; - case psOpDup: - if (sp >= psStackSize) { - goto underflow; - } - if (sp < 1) { - goto overflow; - } - stack[sp - 1] = stack[sp]; - --sp; - break; - case psOpEq: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0; - ++sp; - break; - case psOpExch: - if (sp + 1 >= psStackSize) { - goto underflow; - } - t = stack[sp]; - stack[sp] = stack[sp + 1]; - stack[sp + 1] = t; - break; - case psOpExp: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = pow(stack[sp + 1], stack[sp]); - ++sp; - break; - case psOpFalse: - if (sp < 1) { - goto overflow; - } - stack[sp - 1] = 0; - --sp; - break; - case psOpFloor: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = floor(stack[sp]); - break; - case psOpGe: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0; - ++sp; - break; - case psOpGt: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0; - ++sp; - break; - case psOpIdiv: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp]; - ++sp; - break; - case psOpIndex: - if (sp >= psStackSize) { - goto underflow; - } - k = (int)stack[sp]; - if (k < 0) { - goto invalidArg; - } - if (sp + 1 + k >= psStackSize) { - goto underflow; - } - stack[sp] = stack[sp + 1 + k]; - break; - case psOpLe: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0; - ++sp; - break; - case psOpLn: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = log(stack[sp]); - break; - case psOpLog: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = log10(stack[sp]); - break; - case psOpLt: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0; - ++sp; - break; - case psOpMod: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp]; - ++sp; - break; - case psOpMul: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] * stack[sp]; - ++sp; - break; - case psOpNe: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0; - ++sp; - break; - case psOpNeg: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = -stack[sp]; - break; - case psOpNot: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = stack[sp] == 0 ? 1 : 0; - break; - case psOpOr: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp]; - ++sp; - break; - case psOpPop: - if (sp >= psStackSize) { - goto underflow; - } - ++sp; - break; - case psOpRoll: - if (sp + 1 >= psStackSize) { - goto underflow; - } - k = (int)stack[sp++]; - nn = (int)stack[sp++]; - if (nn < 0) { - goto invalidArg; - } - if (sp + nn > psStackSize) { - goto underflow; - } - if (k >= 0) { - k %= nn; - } else { - k = -k % nn; - if (k) { - k = nn - k; - } - } - for (i = 0; i < nn; ++i) { - tmp[i] = stack[sp + i]; - } - for (i = 0; i < nn; ++i) { - stack[sp + i] = tmp[(i + k) % nn]; - } - break; - case psOpRound: - if (sp >= psStackSize) { - goto underflow; - } - t = stack[sp]; - stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5); - break; - case psOpSin: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = sin(stack[sp]); - break; - case psOpSqrt: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = sqrt(stack[sp]); - break; - case psOpSub: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] - stack[sp]; - ++sp; - break; - case psOpTrue: - if (sp < 1) { - goto overflow; - } - stack[sp - 1] = 1; - --sp; - break; - case psOpTruncate: - if (sp >= psStackSize) { - goto underflow; - } - t = stack[sp]; - stack[sp] = (t >= 0) ? floor(t) : ceil(t); - break; - case psOpXor: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp]; - ++sp; - break; - case psOpPush: - if (sp < 1) { - goto overflow; - } - stack[--sp] = c->val.d; - break; - case psOpJ: - ip = c->val.i; - break; - case psOpJz: - if (sp >= psStackSize) { - goto underflow; - } - k = (int)stack[sp++]; - if (k == 0) { - ip = c->val.i; - } - break; - } - } - return sp; - - underflow: - error(errSyntaxError, -1, "Stack underflow in PostScript function"); - return sp; - overflow: - error(errSyntaxError, -1, "Stack overflow in PostScript function"); - return sp; - invalidArg: - error(errSyntaxError, -1, "Invalid arg in PostScript function"); - return sp; -} diff --git a/test/bug-hunting/cve/CVE-2019-10018/Function.h b/test/bug-hunting/cve/CVE-2019-10018/Function.h deleted file mode 100644 index 615c2abfddf..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10018/Function.h +++ /dev/null @@ -1,310 +0,0 @@ -//======================================================================== -// -// Function.h -// -// Copyright 2001-2003 Glyph & Cog, LLC -// -//======================================================================== - -#ifndef FUNCTION_H -#define FUNCTION_H - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "gtypes.h" -#include "Object.h" - -class GList; -class Dict; -class Stream; -struct PSCode; - -//------------------------------------------------------------------------ -// Function -//------------------------------------------------------------------------ - -#define funcMaxInputs 32 -#define funcMaxOutputs 32 -#define sampledFuncMaxInputs 16 - -class Function { -public: - - Function(); - - virtual ~Function(); - - // Construct a function. Returns NULL if unsuccessful. - static Function *parse(Object *funcObj, int recursion = 0); - - // Initialize the entries common to all function types. - GBool init(Dict *dict); - - virtual Function *copy() = 0; - - // Return the function type: - // -1 : identity - // 0 : sampled - // 2 : exponential - // 3 : stitching - // 4 : PostScript - virtual int getType() = 0; - - // Return size of input and output tuples. - int getInputSize() { - return m; - } - int getOutputSize() { - return n; - } - - double getDomainMin(int i) { - return domain[i][0]; - } - double getDomainMax(int i) { - return domain[i][1]; - } - double getRangeMin(int i) { - return range[i][0]; - } - double getRangeMax(int i) { - return range[i][1]; - } - GBool getHasRange() { - return hasRange; - } - - // Transform an input tuple into an output tuple. - virtual void transform(double *in, double *out) = 0; - - virtual GBool isOk() = 0; - -protected: - - int m, n; // size of input and output tuples - double // min and max values for function domain - domain[funcMaxInputs][2]; - double // min and max values for function range - range[funcMaxOutputs][2]; - GBool hasRange; // set if range is defined -}; - -//------------------------------------------------------------------------ -// IdentityFunction -//------------------------------------------------------------------------ - -class IdentityFunction : public Function { -public: - - IdentityFunction(); - virtual ~IdentityFunction(); - virtual Function *copy() { - return new IdentityFunction(); - } - virtual int getType() { - return -1; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return gTrue; - } - -private: -}; - -//------------------------------------------------------------------------ -// SampledFunction -//------------------------------------------------------------------------ - -class SampledFunction : public Function { -public: - - SampledFunction(Object *funcObj, Dict *dict); - virtual ~SampledFunction(); - virtual Function *copy() { - return new SampledFunction(this); - } - virtual int getType() { - return 0; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - int getSampleSize(int i) { - return sampleSize[i]; - } - double getEncodeMin(int i) { - return encode[i][0]; - } - double getEncodeMax(int i) { - return encode[i][1]; - } - double getDecodeMin(int i) { - return decode[i][0]; - } - double getDecodeMax(int i) { - return decode[i][1]; - } - double *getSamples() { - return samples; - } - -private: - - SampledFunction(SampledFunction *func); - - int // number of samples for each domain element - sampleSize[funcMaxInputs]; - double // min and max values for domain encoder - encode[funcMaxInputs][2]; - double // min and max values for range decoder - decode[funcMaxOutputs][2]; - double // input multipliers - inputMul[funcMaxInputs]; - int *idxOffset; - double *samples; // the samples - int nSamples; // size of the samples array - double *sBuf; // buffer for the transform function - double cacheIn[funcMaxInputs]; - double cacheOut[funcMaxOutputs]; - GBool ok; -}; - -//------------------------------------------------------------------------ -// ExponentialFunction -//------------------------------------------------------------------------ - -class ExponentialFunction : public Function { -public: - - ExponentialFunction(Object *funcObj, Dict *dict); - virtual ~ExponentialFunction(); - virtual Function *copy() { - return new ExponentialFunction(this); - } - virtual int getType() { - return 2; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - double *getC0() { - return c0; - } - double *getC1() { - return c1; - } - double getE() { - return e; - } - -private: - - ExponentialFunction(ExponentialFunction *func); - - double c0[funcMaxOutputs]; - double c1[funcMaxOutputs]; - double e; - GBool ok; -}; - -//------------------------------------------------------------------------ -// StitchingFunction -//------------------------------------------------------------------------ - -class StitchingFunction : public Function { -public: - - StitchingFunction(Object *funcObj, Dict *dict, int recursion); - virtual ~StitchingFunction(); - virtual Function *copy() { - return new StitchingFunction(this); - } - virtual int getType() { - return 3; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - int getNumFuncs() { - return k; - } - Function *getFunc(int i) { - return funcs[i]; - } - double *getBounds() { - return bounds; - } - double *getEncode() { - return encode; - } - double *getScale() { - return scale; - } - -private: - - StitchingFunction(StitchingFunction *func); - - int k; - Function **funcs; - double *bounds; - double *encode; - double *scale; - GBool ok; -}; - -//------------------------------------------------------------------------ -// PostScriptFunction -//------------------------------------------------------------------------ - -class PostScriptFunction : public Function { -public: - - PostScriptFunction(Object *funcObj, Dict *dict); - virtual ~PostScriptFunction(); - virtual Function *copy() { - return new PostScriptFunction(this); - } - virtual int getType() { - return 4; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - GString *getCodeString() { - return codeString; - } - -private: - - PostScriptFunction(PostScriptFunction *func); - GBool parseCode(GList *tokens, int *tokPtr, int *codePtr); - void addCode(int *codePtr, int op); - void addCodeI(int *codePtr, int op, int x); - void addCodeD(int *codePtr, int op, double x); - GString *getToken(Stream *str); - int exec(double *stack, int sp0); - - GString *codeString; - PSCode *code; - int codeLen; - int codeSize; - double cacheIn[funcMaxInputs]; - double cacheOut[funcMaxOutputs]; - GBool ok; -}; - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-10018/expected.txt b/test/bug-hunting/cve/CVE-2019-10018/expected.txt deleted file mode 100644 index 851e52fdd8c..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10018/expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -Function.cc:1374:bughuntingDivByZero - diff --git a/test/bug-hunting/cve/CVE-2019-10019/PSOutputDev.cc b/test/bug-hunting/cve/CVE-2019-10019/PSOutputDev.cc deleted file mode 100644 index 15a2a233990..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10019/PSOutputDev.cc +++ /dev/null @@ -1,8386 +0,0 @@ -//======================================================================== -// -// PSOutputDev.cc -// -// Copyright 1996-2013 Glyph & Cog, LLC -// -//======================================================================== - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include -#include -#include -#include -#include -#include "gmempp.h" -#include "GString.h" -#include "GList.h" -#include "GHash.h" -#include "config.h" -#include "GlobalParams.h" -#include "Object.h" -#include "Error.h" -#include "Function.h" -#include "Gfx.h" -#include "GfxState.h" -#include "GfxFont.h" -#include "UnicodeMap.h" -#include "FoFiType1C.h" -#include "FoFiTrueType.h" -#include "Catalog.h" -#include "Page.h" -#include "Stream.h" -#include "Annot.h" -#include "PDFDoc.h" -#include "XRef.h" -#include "PreScanOutputDev.h" -#include "CharCodeToUnicode.h" -#include "Form.h" -#include "TextString.h" -#if HAVE_SPLASH -# include "Splash.h" -# include "SplashBitmap.h" -# include "SplashOutputDev.h" -#endif -#include "PSOutputDev.h" - -// the MSVC math.h doesn't define this -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -//------------------------------------------------------------------------ -// PostScript prolog and setup -//------------------------------------------------------------------------ - -// The '~' escapes mark prolog code that is emitted only in certain -// levels: -// -// ~[123][ngs] -// ^ ^----- n=psLevel_, g=psLevel_Gray, s=psLevel_Sep -// +----- 1=psLevel1__, 2=psLevel2__, 3=psLevel3__ - -static const char *prolog[] = { - "/xpdf 75 dict def xpdf begin", - "% PDF special state", - "/pdfDictSize 15 def", - "~1ns", - "/pdfStates 64 array def", - " 0 1 63 {", - " pdfStates exch pdfDictSize dict", - " dup /pdfStateIdx 3 index put", - " put", - " } for", - "~123ngs", - "/pdfSetup {", - " /pdfDuplex exch def", - " /setpagedevice where {", - " pop 2 dict begin", - " /Policies 1 dict dup begin /PageSize 6 def end def", - " pdfDuplex { /Duplex true def } if", - " currentdict end setpagedevice", - " } if", - " /pdfPageW 0 def", - " /pdfPageH 0 def", - "} def", - "/pdfSetupPaper {", - " 2 copy pdfPageH ne exch pdfPageW ne or {", - " /pdfPageH exch def", - " /pdfPageW exch def", - " /setpagedevice where {", - " pop 3 dict begin", - " /PageSize [pdfPageW pdfPageH] def", - " pdfDuplex { /Duplex true def } if", - " /ImagingBBox null def", - " currentdict end setpagedevice", - " } if", - " } {", - " pop pop", - " } ifelse", - "} def", - "~1ns", - "/pdfOpNames [", - " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke", - " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender", - " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath", - "] def", - "~123ngs", - "/pdfStartPage {", - "~1ns", - " pdfStates 0 get begin", - "~23ngs", - " pdfDictSize dict begin", - "~23n", - " /pdfFillCS [] def", - " /pdfFillXform {} def", - " /pdfStrokeCS [] def", - " /pdfStrokeXform {} def", - "~1n", - " /pdfFill 0 def", - " /pdfStroke 0 def", - "~1s", - " /pdfFill [0 0 0 1] def", - " /pdfStroke [0 0 0 1] def", - "~23g", - " /pdfFill 0 def", - " /pdfStroke 0 def", - "~23ns", - " /pdfFill [0] def", - " /pdfStroke [0] def", - " /pdfFillOP false def", - " /pdfStrokeOP false def", - "~123ngs", - " /pdfLastFill false def", - " /pdfLastStroke false def", - " /pdfTextMat [1 0 0 1 0 0] def", - " /pdfFontSize 0 def", - " /pdfCharSpacing 0 def", - " /pdfTextRender 0 def", - " /pdfTextRise 0 def", - " /pdfWordSpacing 0 def", - " /pdfHorizScaling 1 def", - " /pdfTextClipPath [] def", - "} def", - "/pdfEndPage { end } def", - "~23s", - "% separation convention operators", - "/findcmykcustomcolor where {", - " pop", - "}{", - " /findcmykcustomcolor { 5 array astore } def", - "} ifelse", - "/setcustomcolor where {", - " pop", - "}{", - " /setcustomcolor {", - " exch", - " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", - " 0 4 getinterval cvx", - " [ exch /dup load exch { mul exch dup } /forall load", - " /pop load dup ] cvx", - " ] setcolorspace setcolor", - " } def", - "} ifelse", - "/customcolorimage where {", - " pop", - "}{", - " /customcolorimage {", - " gsave", - " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", - " 0 4 getinterval", - " [ exch /dup load exch { mul exch dup } /forall load", - " /pop load dup ] cvx", - " ] setcolorspace", - " 10 dict begin", - " /ImageType 1 def", - " /DataSource exch def", - " /ImageMatrix exch def", - " /BitsPerComponent exch def", - " /Height exch def", - " /Width exch def", - " /Decode [1 0] def", - " currentdict end", - " image", - " grestore", - " } def", - "} ifelse", - "~123ngs", - "% PDF color state", - "~1n", - "/g { dup /pdfFill exch def setgray", - " /pdfLastFill true def /pdfLastStroke false def } def", - "/G { dup /pdfStroke exch def setgray", - " /pdfLastStroke true def /pdfLastFill false def } def", - "/fCol {", - " pdfLastFill not {", - " pdfFill setgray", - " /pdfLastFill true def /pdfLastStroke false def", - " } if", - "} def", - "/sCol {", - " pdfLastStroke not {", - " pdfStroke setgray", - " /pdfLastStroke true def /pdfLastFill false def", - " } if", - "} def", - "~1s", - "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", - " /pdfLastFill true def /pdfLastStroke false def } def", - "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", - " /pdfLastStroke true def /pdfLastFill false def } def", - "/fCol {", - " pdfLastFill not {", - " pdfFill aload pop setcmykcolor", - " /pdfLastFill true def /pdfLastStroke false def", - " } if", - "} def", - "/sCol {", - " pdfLastStroke not {", - " pdfStroke aload pop setcmykcolor", - " /pdfLastStroke true def /pdfLastFill false def", - " } if", - "} def", - "~23n", - "/cs { /pdfFillXform exch def dup /pdfFillCS exch def", - " setcolorspace } def", - "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def", - " setcolorspace } def", - "/sc { pdfLastFill not {", - " pdfFillCS setcolorspace pdfFillOP setoverprint", - " } if", - " dup /pdfFill exch def aload pop pdfFillXform setcolor", - " /pdfLastFill true def /pdfLastStroke false def } def", - "/SC { pdfLastStroke not {", - " pdfStrokeCS setcolorspace pdfStrokeOP setoverprint", - " } if", - " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor", - " /pdfLastStroke true def /pdfLastFill false def } def", - "/op { /pdfFillOP exch def", - " pdfLastFill { pdfFillOP setoverprint } if } def", - "/OP { /pdfStrokeOP exch def", - " pdfLastStroke { pdfStrokeOP setoverprint } if } def", - "/fCol {", - " pdfLastFill not {", - " pdfFillCS setcolorspace", - " pdfFill aload pop pdfFillXform setcolor", - " pdfFillOP setoverprint", - " /pdfLastFill true def /pdfLastStroke false def", - " } if", - "} def", - "/sCol {", - " pdfLastStroke not {", - " pdfStrokeCS setcolorspace", - " pdfStroke aload pop pdfStrokeXform setcolor", - " pdfStrokeOP setoverprint", - " /pdfLastStroke true def /pdfLastFill false def", - " } if", - "} def", - "~23g", - "/g { dup /pdfFill exch def setgray", - " /pdfLastFill true def /pdfLastStroke false def } def", - "/G { dup /pdfStroke exch def setgray", - " /pdfLastStroke true def /pdfLastFill false def } def", - "/fCol {", - " pdfLastFill not {", - " pdfFill setgray", - " /pdfLastFill true def /pdfLastStroke false def", - " } if", - "} def", - "/sCol {", - " pdfLastStroke not {", - " pdfStroke setgray", - " /pdfLastStroke true def /pdfLastFill false def", - " } if", - "} def", - "~23s", - "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", - " pdfFillOP setoverprint", - " /pdfLastFill true def /pdfLastStroke false def } def", - "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", - " pdfStrokeOP setoverprint", - " /pdfLastStroke true def /pdfLastFill false def } def", - "/ck { 6 copy 6 array astore /pdfFill exch def", - " findcmykcustomcolor exch setcustomcolor", - " pdfFillOP setoverprint", - " /pdfLastFill true def /pdfLastStroke false def } def", - "/CK { 6 copy 6 array astore /pdfStroke exch def", - " findcmykcustomcolor exch setcustomcolor", - " pdfStrokeOP setoverprint", - " /pdfLastStroke true def /pdfLastFill false def } def", - "/op { /pdfFillOP exch def", - " pdfLastFill { pdfFillOP setoverprint } if } def", - "/OP { /pdfStrokeOP exch def", - " pdfLastStroke { pdfStrokeOP setoverprint } if } def", - "/fCol {", - " pdfLastFill not {", - " pdfFill aload length 4 eq {", - " setcmykcolor", - " }{", - " findcmykcustomcolor exch setcustomcolor", - " } ifelse", - " pdfFillOP setoverprint", - " /pdfLastFill true def /pdfLastStroke false def", - " } if", - "} def", - "/sCol {", - " pdfLastStroke not {", - " pdfStroke aload length 4 eq {", - " setcmykcolor", - " }{", - " findcmykcustomcolor exch setcustomcolor", - " } ifelse", - " pdfStrokeOP setoverprint", - " /pdfLastStroke true def /pdfLastFill false def", - " } if", - "} def", - "~3ns", - "/opm {", - " /setoverprintmode where { pop setoverprintmode } { pop } ifelse", - "} def", - "~123ngs", - "% build a font", - "/pdfMakeFont {", - " 4 3 roll findfont", - " 4 2 roll matrix scale makefont", - " dup length dict begin", - " { 1 index /FID ne { def } { pop pop } ifelse } forall", - " /Encoding exch def", - " currentdict", - " end", - " definefont pop", - "} def", - "/pdfMakeFont16 {", - " exch findfont", - " dup length dict begin", - " { 1 index /FID ne { def } { pop pop } ifelse } forall", - " /WMode exch def", - " currentdict", - " end", - " definefont pop", - "} def", - "~3ngs", - "/pdfMakeFont16L3 {", - " 1 index /CIDFont resourcestatus {", - " pop pop 1 index /CIDFont findresource /CIDFontType known", - " } {", - " false", - " } ifelse", - " {", - " 0 eq { /Identity-H } { /Identity-V } ifelse", - " exch 1 array astore composefont pop", - " } {", - " pdfMakeFont16", - " } ifelse", - "} def", - "~123ngs", - "% graphics state operators", - "~1ns", - "/q {", - " gsave", - " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for", - " pdfStates pdfStateIdx 1 add get begin", - " pdfOpNames { exch def } forall", - "} def", - "/Q { end grestore } def", - "~23ngs", - "/q { gsave pdfDictSize dict begin } def", - "/Q {", - " end grestore", - "} def", - "~123ngs", - "/cm { concat } def", - "/d { setdash } def", - "/i { setflat } def", - "/j { setlinejoin } def", - "/J { setlinecap } def", - "/M { setmiterlimit } def", - "/w { setlinewidth } def", - "% path segment operators", - "/m { moveto } def", - "/l { lineto } def", - "/c { curveto } def", - "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto", - " neg 0 rlineto closepath } def", - "/h { closepath } def", - "% path painting operators", - "/S { sCol stroke } def", - "/Sf { fCol stroke } def", - "/f { fCol fill } def", - "/f* { fCol eofill } def", - "% clipping operators", - "/W { clip newpath } def", - "/W* { eoclip newpath } def", - "/Ws { strokepath clip newpath } def", - "% text state operators", - "/Tc { /pdfCharSpacing exch def } def", - "/Tf { dup /pdfFontSize exch def", - " dup pdfHorizScaling mul exch matrix scale", - " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put", - " exch findfont exch makefont setfont } def", - "/Tr { /pdfTextRender exch def } def", - "/Ts { /pdfTextRise exch def } def", - "/Tw { /pdfWordSpacing exch def } def", - "/Tz { /pdfHorizScaling exch def } def", - "% text positioning operators", - "/Td { pdfTextMat transform moveto } def", - "/Tm { /pdfTextMat exch def } def", - "% text string operators", - "/xyshow where {", - " pop", - " /xyshow2 {", - " dup length array", - " 0 2 2 index length 1 sub {", - " 2 index 1 index 2 copy get 3 1 roll 1 add get", - " pdfTextMat dtransform", - " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put", - " } for", - " exch pop", - " xyshow", - " } def", - "}{", - " /xyshow2 {", - " currentfont /FontType get 0 eq {", - " 0 2 3 index length 1 sub {", - " currentpoint 4 index 3 index 2 getinterval show moveto", - " 2 copy get 2 index 3 2 roll 1 add get", - " pdfTextMat dtransform rmoveto", - " } for", - " } {", - " 0 1 3 index length 1 sub {", - " currentpoint 4 index 3 index 1 getinterval show moveto", - " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", - " pdfTextMat dtransform rmoveto", - " } for", - " } ifelse", - " pop pop", - " } def", - "} ifelse", - "/cshow where {", - " pop", - " /xycp {", // xycharpath - " 0 3 2 roll", - " {", - " pop pop currentpoint 3 2 roll", - " 1 string dup 0 4 3 roll put false charpath moveto", - " 2 copy get 2 index 2 index 1 add get", - " pdfTextMat dtransform rmoveto", - " 2 add", - " } exch cshow", - " pop pop", - " } def", - "}{", - " /xycp {", // xycharpath - " currentfont /FontType get 0 eq {", - " 0 2 3 index length 1 sub {", - " currentpoint 4 index 3 index 2 getinterval false charpath moveto", - " 2 copy get 2 index 3 2 roll 1 add get", - " pdfTextMat dtransform rmoveto", - " } for", - " } {", - " 0 1 3 index length 1 sub {", - " currentpoint 4 index 3 index 1 getinterval false charpath moveto", - " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", - " pdfTextMat dtransform rmoveto", - " } for", - " } ifelse", - " pop pop", - " } def", - "} ifelse", - "/Tj {", - " fCol", // because stringwidth has to draw Type 3 chars - " 0 pdfTextRise pdfTextMat dtransform rmoveto", - " currentpoint 4 2 roll", - " pdfTextRender 1 and 0 eq {", - " 2 copy xyshow2", - " } if", - " pdfTextRender 3 and dup 1 eq exch 2 eq or {", - " 3 index 3 index moveto", - " 2 copy", - " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", - " xycp currentpoint stroke moveto", - " } if", - " pdfTextRender 4 and 0 ne {", - " 4 2 roll moveto xycp", - " /pdfTextClipPath [ pdfTextClipPath aload pop", - " {/moveto cvx}", - " {/lineto cvx}", - " {/curveto cvx}", - " {/closepath cvx}", - " pathforall ] def", - " currentpoint newpath moveto", - " } {", - " pop pop pop pop", - " } ifelse", - " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", - "} def", - "/Tj3 {", - " pdfTextRender 3 and 3 ne {" - " fCol", // because stringwidth has to draw Type 3 chars - " 0 pdfTextRise pdfTextMat dtransform rmoveto", - " xyshow2", - " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", - " } {", - " pop pop", - " } ifelse", - "} def", - "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0", - " pdfTextMat dtransform rmoveto } def", - "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch", - " pdfTextMat dtransform rmoveto } def", - "/Tclip { pdfTextClipPath cvx exec clip newpath", - " /pdfTextClipPath [] def } def", - "~1ns", - "% Level 1 image operators", - "~1n", - "/pdfIm1 {", - " /pdfImBuf1 4 index string def", - " { currentfile pdfImBuf1 readhexstring pop } image", - "} def", - "~1s", - "/pdfIm1Sep {", - " /pdfImBuf1 4 index string def", - " /pdfImBuf2 4 index string def", - " /pdfImBuf3 4 index string def", - " /pdfImBuf4 4 index string def", - " { currentfile pdfImBuf1 readhexstring pop }", - " { currentfile pdfImBuf2 readhexstring pop }", - " { currentfile pdfImBuf3 readhexstring pop }", - " { currentfile pdfImBuf4 readhexstring pop }", - " true 4 colorimage", - "} def", - "~1ns", - "/pdfImM1 {", - " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", - " { currentfile pdfImBuf1 readhexstring pop } imagemask", - "} def", - "/pdfImStr {", - " 2 copy exch length lt {", - " 2 copy get exch 1 add exch", - " } {", - " ()", - " } ifelse", - "} def", - "/pdfImM1a {", - " { pdfImStr } imagemask", - " pop pop", - "} def", - "~23ngs", - "% Level 2/3 image operators", - "/pdfImBuf 100 string def", - "/pdfImStr {", - " 2 copy exch length lt {", - " 2 copy get exch 1 add exch", - " } {", - " ()", - " } ifelse", - "} def", - "/skipEOD {", - " { currentfile pdfImBuf readline", - " not { pop exit } if", - " (%-EOD-) eq { exit } if } loop", - "} def", - "/pdfIm { image skipEOD } def", - "~3ngs", - "/pdfMask {", - " /ReusableStreamDecode filter", - " skipEOD", - " /maskStream exch def", - "} def", - "/pdfMaskEnd { maskStream closefile } def", - "/pdfMaskInit {", - " /maskArray exch def", - " /maskIdx 0 def", - "} def", - "/pdfMaskSrc {", - " maskIdx maskArray length lt {", - " maskArray maskIdx get", - " /maskIdx maskIdx 1 add def", - " } {", - " ()", - " } ifelse", - "} def", - "~23s", - "/pdfImSep {", - " findcmykcustomcolor exch", - " dup /Width get /pdfImBuf1 exch string def", - " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def", - " /pdfImDecodeLow exch def", - " begin Width Height BitsPerComponent ImageMatrix DataSource end", - " /pdfImData exch def", - " { pdfImData pdfImBuf1 readstring pop", - " 0 1 2 index length 1 sub {", - " 1 index exch 2 copy get", - " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi", - " 255 exch sub put", - " } for }", - " 6 5 roll customcolorimage", - " skipEOD", - "} def", - "~23ngs", - "/pdfImM { fCol imagemask skipEOD } def", - "/pr {", - " 4 2 roll exch 5 index div exch 4 index div moveto", - " exch 3 index div dup 0 rlineto", - " exch 2 index div 0 exch rlineto", - " neg 0 rlineto", - " closepath", - "} def", - "/pdfImClip { gsave clip } def", - "/pdfImClipEnd { grestore } def", - "~23ns", - "% shading operators", - "/colordelta {", - " false 0 1 3 index length 1 sub {", - " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {", - " pop true", - " } if", - " } for", - " exch pop exch pop", - "} def", - "/funcCol { func n array astore } def", - "/funcSH {", - " dup 0 eq {", - " true", - " } {", - " dup 6 eq {", - " false", - " } {", - " 4 index 4 index funcCol dup", - " 6 index 4 index funcCol dup", - " 3 1 roll colordelta 3 1 roll", - " 5 index 5 index funcCol dup", - " 3 1 roll colordelta 3 1 roll", - " 6 index 8 index funcCol dup", - " 3 1 roll colordelta 3 1 roll", - " colordelta or or or", - " } ifelse", - " } ifelse", - " {", - " 1 add", - " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch", - " 6 index 6 index 4 index 4 index 4 index funcSH", - " 2 index 6 index 6 index 4 index 4 index funcSH", - " 6 index 2 index 4 index 6 index 4 index funcSH", - " 5 3 roll 3 2 roll funcSH pop pop", - " } {", - " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul", - "~23n", - " funcCol sc", - "~23s", - " funcCol aload pop k", - "~23ns", - " dup 4 index exch mat transform m", - " 3 index 3 index mat transform l", - " 1 index 3 index mat transform l", - " mat transform l pop pop h f*", - " } ifelse", - "} def", - "/axialCol {", - " dup 0 lt {", - " pop t0", - " } {", - " dup 1 gt {", - " pop t1", - " } {", - " dt mul t0 add", - " } ifelse", - " } ifelse", - " func n array astore", - "} def", - "/axialSH {", - " dup 2 lt {", - " true", - " } {", - " dup 8 eq {", - " false", - " } {", - " 2 index axialCol 2 index axialCol colordelta", - " } ifelse", - " } ifelse", - " {", - " 1 add 3 1 roll 2 copy add 0.5 mul", - " dup 4 3 roll exch 4 index axialSH", - " exch 3 2 roll axialSH", - " } {", - " pop 2 copy add 0.5 mul", - "~23n", - " axialCol sc", - "~23s", - " axialCol aload pop k", - "~23ns", - " exch dup dx mul x0 add exch dy mul y0 add", - " 3 2 roll dup dx mul x0 add exch dy mul y0 add", - " dx abs dy abs ge {", - " 2 copy yMin sub dy mul dx div add yMin m", - " yMax sub dy mul dx div add yMax l", - " 2 copy yMax sub dy mul dx div add yMax l", - " yMin sub dy mul dx div add yMin l", - " h f*", - " } {", - " exch 2 copy xMin sub dx mul dy div add xMin exch m", - " xMax sub dx mul dy div add xMax exch l", - " exch 2 copy xMax sub dx mul dy div add xMax exch l", - " xMin sub dx mul dy div add xMin exch l", - " h f*", - " } ifelse", - " } ifelse", - "} def", - "/radialCol {", - " dup t0 lt {", - " pop t0", - " } {", - " dup t1 gt {", - " pop t1", - " } if", - " } ifelse", - " func n array astore", - "} def", - "/radialSH {", - " dup 0 eq {", - " true", - " } {", - " dup 8 eq {", - " false", - " } {", - " 2 index dt mul t0 add radialCol", - " 2 index dt mul t0 add radialCol colordelta", - " } ifelse", - " } ifelse", - " {", - " 1 add 3 1 roll 2 copy add 0.5 mul", - " dup 4 3 roll exch 4 index radialSH", - " exch 3 2 roll radialSH", - " } {", - " pop 2 copy add 0.5 mul dt mul t0 add", - "~23n", - " radialCol sc", - "~23s", - " radialCol aload pop k", - "~23ns", - " encl {", - " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", - " 0 360 arc h", - " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", - " 360 0 arcn h f", - " } {", - " 2 copy", - " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", - " a1 a2 arcn", - " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", - " a2 a1 arcn h", - " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", - " a1 a2 arc", - " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", - " a2 a1 arc h f", - " } ifelse", - " } ifelse", - "} def", - "~123ngs", - "end", - NULL -}; - -static const char *minLineWidthProlog[] = { - "/pdfDist { dup dtransform dup mul exch dup mul add 0.5 mul sqrt } def", - "/pdfIDist { dup idtransform dup mul exch dup mul add 0.5 mul sqrt } def", - "/pdfMinLineDist pdfMinLineWidth pdfDist def", - "/setlinewidth {", - " dup pdfDist pdfMinLineDist lt {", - " pop pdfMinLineDist pdfIDist", - " } if", - " setlinewidth", - "} bind def", - NULL -}; - -static const char *cmapProlog[] = { - "/CIDInit /ProcSet findresource begin", - "10 dict begin", - " begincmap", - " /CMapType 1 def", - " /CMapName /Identity-H def", - " /CIDSystemInfo 3 dict dup begin", - " /Registry (Adobe) def", - " /Ordering (Identity) def", - " /Supplement 0 def", - " end def", - " 1 begincodespacerange", - " <0000> ", - " endcodespacerange", - " 0 usefont", - " 1 begincidrange", - " <0000> 0", - " endcidrange", - " endcmap", - " currentdict CMapName exch /CMap defineresource pop", - "end", - "10 dict begin", - " begincmap", - " /CMapType 1 def", - " /CMapName /Identity-V def", - " /CIDSystemInfo 3 dict dup begin", - " /Registry (Adobe) def", - " /Ordering (Identity) def", - " /Supplement 0 def", - " end def", - " /WMode 1 def", - " 1 begincodespacerange", - " <0000> ", - " endcodespacerange", - " 0 usefont", - " 1 begincidrange", - " <0000> 0", - " endcidrange", - " endcmap", - " currentdict CMapName exch /CMap defineresource pop", - "end", - "end", - NULL -}; - -//------------------------------------------------------------------------ -// Fonts -//------------------------------------------------------------------------ - -struct PSSubstFont { - const char *psName; // PostScript name - double mWidth; // width of 'm' character -}; - -// NB: must be in same order as base14SubstFonts in GfxFont.cc -static PSSubstFont psBase14SubstFonts[14] = { - {"Courier", 0.600}, - {"Courier-Oblique", 0.600}, - {"Courier-Bold", 0.600}, - {"Courier-BoldOblique", 0.600}, - {"Helvetica", 0.833}, - {"Helvetica-Oblique", 0.833}, - {"Helvetica-Bold", 0.889}, - {"Helvetica-BoldOblique", 0.889}, - {"Times-Roman", 0.788}, - {"Times-Italic", 0.722}, - {"Times-Bold", 0.833}, - {"Times-BoldItalic", 0.778}, - // the last two are never used for substitution - {"Symbol", 0}, - {"ZapfDingbats", 0} -}; - -class PSFontInfo { -public: - - PSFontInfo(Ref fontIDA) - { fontID = fontIDA; ff = NULL; } - - Ref fontID; - PSFontFileInfo *ff; // pointer to font file info; NULL indicates - // font mapping failed -}; - -enum PSFontFileLocation { - psFontFileResident, - psFontFileEmbedded, - psFontFileExternal -}; - -class PSFontFileInfo { -public: - - PSFontFileInfo(GString *psNameA, GfxFontType typeA, - PSFontFileLocation locA); - ~PSFontFileInfo(); - - GString *psName; // name under which font is defined - GfxFontType type; // font type - PSFontFileLocation loc; // font location - Ref embFontID; // object ID for the embedded font file - // (for all embedded fonts) - GString *extFileName; // external font file path - // (for all external fonts) - GString *encoding; // encoding name (for resident CID fonts) - int *codeToGID; // mapping from code/CID to GID - // (for TrueType, OpenType-TrueType, and - // CID OpenType-CFF fonts) - int codeToGIDLen; // length of codeToGID array -}; - -PSFontFileInfo::PSFontFileInfo(GString *psNameA, GfxFontType typeA, - PSFontFileLocation locA) { - psName = psNameA; - type = typeA; - loc = locA; - embFontID.num = embFontID.gen = -1; - extFileName = NULL; - encoding = NULL; - codeToGID = NULL; - codeToGIDLen = 0; -} - -PSFontFileInfo::~PSFontFileInfo() { - delete psName; - if (extFileName) { - delete extFileName; - } - if (encoding) { - delete encoding; - } - if (codeToGID) { - gfree(codeToGID); - } -} - -//------------------------------------------------------------------------ -// process colors -//------------------------------------------------------------------------ - -#define psProcessCyan 1 -#define psProcessMagenta 2 -#define psProcessYellow 4 -#define psProcessBlack 8 -#define psProcessCMYK 15 - -//------------------------------------------------------------------------ -// PSOutCustomColor -//------------------------------------------------------------------------ - -class PSOutCustomColor { -public: - - PSOutCustomColor(double cA, double mA, - double yA, double kA, GString *nameA); - ~PSOutCustomColor(); - - double c, m, y, k; - GString *name; - PSOutCustomColor *next; -}; - -PSOutCustomColor::PSOutCustomColor(double cA, double mA, - double yA, double kA, GString *nameA) { - c = cA; - m = mA; - y = yA; - k = kA; - name = nameA; - next = NULL; -} - -PSOutCustomColor::~PSOutCustomColor() { - delete name; -} - -//------------------------------------------------------------------------ - -struct PSOutImgClipRect { - int x0, x1, y0, y1; -}; - -//------------------------------------------------------------------------ - -struct PSOutPaperSize { - PSOutPaperSize(int wA, int hA) { w = wA; h = hA; } - int w, h; -}; - -//------------------------------------------------------------------------ -// DeviceNRecoder -//------------------------------------------------------------------------ - -class DeviceNRecoder: public FilterStream { -public: - - DeviceNRecoder(Stream *strA, int widthA, int heightA, - GfxImageColorMap *colorMapA); - virtual ~DeviceNRecoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { return strWeird; } - virtual void reset(); - virtual void close(); - virtual int getChar() - { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } - virtual int lookChar() - { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } - virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } - virtual GBool isBinary(GBool last = gTrue) { return gTrue; } - virtual GBool isEncoder() { return gTrue; } - -private: - - GBool fillBuf(); - - int width, height; - GfxImageColorMap *colorMap; - Function *func; - ImageStream *imgStr; - int buf[gfxColorMaxComps]; - int pixelIdx; - int bufIdx; - int bufSize; -}; - -DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA, - GfxImageColorMap *colorMapA): - FilterStream(strA) { - width = widthA; - height = heightA; - colorMap = colorMapA; - imgStr = NULL; - pixelIdx = 0; - bufIdx = gfxColorMaxComps; - bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> - getAlt()->getNComps(); - func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> - getTintTransformFunc(); -} - -DeviceNRecoder::~DeviceNRecoder() { - if (str->isEncoder()) { - delete str; - } -} - -Stream *DeviceNRecoder::copy() { - error(errInternal, -1, "Called copy() on DeviceNRecoder"); - return NULL; -} - -void DeviceNRecoder::reset() { - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); -} - -void DeviceNRecoder::close() { - delete imgStr; - imgStr = NULL; - str->close(); -} - -GBool DeviceNRecoder::fillBuf() { - Guchar pixBuf[gfxColorMaxComps]; - GfxColor color; - double x[gfxColorMaxComps], y[gfxColorMaxComps]; - int i; - - if (pixelIdx >= width * height) { - return gFalse; - } - imgStr->getPixel(pixBuf); - colorMap->getColor(pixBuf, &color); - for (i = 0; - i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps(); - ++i) { - x[i] = colToDbl(color.c[i]); - } - func->transform(x, y); - for (i = 0; i < bufSize; ++i) { - buf[i] = (int)(y[i] * 255 + 0.5); - } - bufIdx = 0; - ++pixelIdx; - return gTrue; -} - -//------------------------------------------------------------------------ -// GrayRecoder -//------------------------------------------------------------------------ - -class GrayRecoder: public FilterStream { -public: - - GrayRecoder(Stream *strA, int widthA, int heightA, - GfxImageColorMap *colorMapA); - virtual ~GrayRecoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { return strWeird; } - virtual void reset(); - virtual void close(); - virtual int getChar() - { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx++]; } - virtual int lookChar() - { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx]; } - virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } - virtual GBool isBinary(GBool last = gTrue) { return gTrue; } - virtual GBool isEncoder() { return gTrue; } - -private: - - GBool fillBuf(); - - int width, height; - GfxImageColorMap *colorMap; - ImageStream *imgStr; - Guchar *buf; - int bufIdx; -}; - -GrayRecoder::GrayRecoder(Stream *strA, int widthA, int heightA, - GfxImageColorMap *colorMapA): - FilterStream(strA) { - width = widthA; - height = heightA; - colorMap = colorMapA; - imgStr = NULL; - buf = (Guchar *)gmalloc(width); - bufIdx = width; -} - -GrayRecoder::~GrayRecoder() { - gfree(buf); - if (str->isEncoder()) { - delete str; - } -} - -Stream *GrayRecoder::copy() { - error(errInternal, -1, "Called copy() on GrayRecoder"); - return NULL; -} - -void GrayRecoder::reset() { - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); -} - -void GrayRecoder::close() { - delete imgStr; - imgStr = NULL; - str->close(); -} - -GBool GrayRecoder::fillBuf() { - Guchar *line; - - if (!(line = imgStr->getLine())) { - bufIdx = width; - return gFalse; - } - //~ this should probably use the rendering intent from the image - //~ dict, or from the content stream - colorMap->getGrayByteLine(line, buf, width, - gfxRenderingIntentRelativeColorimetric); - bufIdx = 0; - return gTrue; -} - -//------------------------------------------------------------------------ -// ColorKeyToMaskEncoder -//------------------------------------------------------------------------ - -class ColorKeyToMaskEncoder: public FilterStream { -public: - - ColorKeyToMaskEncoder(Stream *strA, int widthA, int heightA, - GfxImageColorMap *colorMapA, int *maskColorsA); - virtual ~ColorKeyToMaskEncoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { return strWeird; } - virtual void reset(); - virtual void close(); - virtual int getChar() - { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } - virtual int lookChar() - { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } - virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } - virtual GBool isBinary(GBool last = gTrue) { return gTrue; } - virtual GBool isEncoder() { return gTrue; } - -private: - - GBool fillBuf(); - - int width, height; - GfxImageColorMap *colorMap; - int numComps; - int *maskColors; - ImageStream *imgStr; - Guchar *buf; - int bufIdx; - int bufSize; -}; - -ColorKeyToMaskEncoder::ColorKeyToMaskEncoder(Stream *strA, - int widthA, int heightA, - GfxImageColorMap *colorMapA, - int *maskColorsA): - FilterStream(strA) -{ - width = widthA; - height = heightA; - colorMap = colorMapA; - numComps = colorMap->getNumPixelComps(); - maskColors = maskColorsA; - imgStr = NULL; - bufSize = (width + 7) / 8; - buf = (Guchar *)gmalloc(bufSize); - bufIdx = width; -} - -ColorKeyToMaskEncoder::~ColorKeyToMaskEncoder() { - gfree(buf); - if (str->isEncoder()) { - delete str; - } -} - -Stream *ColorKeyToMaskEncoder::copy() { - error(errInternal, -1, "Called copy() on ColorKeyToMaskEncoder"); - return NULL; -} - -void ColorKeyToMaskEncoder::reset() { - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); -} - -void ColorKeyToMaskEncoder::close() { - delete imgStr; - imgStr = NULL; - str->close(); -} - -GBool ColorKeyToMaskEncoder::fillBuf() { - Guchar *line, *linePtr, *bufPtr; - Guchar byte; - int x, xx, i; - - if (!(line = imgStr->getLine())) { - bufIdx = width; - return gFalse; - } - linePtr = line; - bufPtr = buf; - for (x = 0; x < width; x += 8) { - byte = 0; - for (xx = 0; xx < 8; ++xx) { - byte = (Guchar)(byte << 1); - if (x + xx < width) { - for (i = 0; i < numComps; ++i) { - if (linePtr[i] < maskColors[2 * i] || - linePtr[i] > maskColors[2 * i + 1]) { - break; - } - } - if (i >= numComps) { - byte |= 1; - } - linePtr += numComps; - } else { - byte |= 1; - } - } - *bufPtr++ = byte; - } - bufIdx = 0; - return gTrue; -} - -//------------------------------------------------------------------------ -// PSOutputDev -//------------------------------------------------------------------------ - -extern "C" { -typedef void (*SignalFunc)(int); -} - -static void outputToFile(void *stream, const char *data, int len) { - fwrite(data, 1, len, (FILE *)stream); -} - -PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA, - int firstPageA, int lastPageA, PSOutMode modeA, - int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, - GBool manualCtrlA, - PSOutCustomCodeCbk customCodeCbkA, - void *customCodeCbkDataA, - GBool honorUserUnitA) { - FILE *f; - PSFileType fileTypeA; - - underlayCbk = NULL; - underlayCbkData = NULL; - overlayCbk = NULL; - overlayCbkData = NULL; - customCodeCbk = customCodeCbkA; - customCodeCbkData = customCodeCbkDataA; - - rasterizePage = NULL; - fontInfo = new GList(); - fontFileInfo = new GHash(); - imgIDs = NULL; - formIDs = NULL; - visitedResources = NULL; - saveStack = NULL; - paperSizes = NULL; - embFontList = NULL; - customColors = NULL; - haveTextClip = gFalse; - t3String = NULL; - - // open file or pipe - if (!strcmp(fileName, "-")) { - fileTypeA = psStdout; - f = stdout; - } else if (fileName[0] == '|') { - fileTypeA = psPipe; -#ifdef HAVE_POPEN -#ifndef _WIN32 - signal(SIGPIPE, (SignalFunc)SIG_IGN); -#endif - if (!(f = popen(fileName + 1, "w"))) { - error(errIO, -1, "Couldn't run print command '{0:s}'", fileName); - ok = gFalse; - return; - } -#else - error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName); - ok = gFalse; - return; -#endif - } else { - fileTypeA = psFile; - if (!(f = fopen(fileName, "w"))) { - error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName); - ok = gFalse; - return; - } - } - - init(outputToFile, f, fileTypeA, - docA, firstPageA, lastPageA, modeA, - imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, honorUserUnitA); -} - -PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, - PDFDoc *docA, - int firstPageA, int lastPageA, PSOutMode modeA, - int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, - GBool manualCtrlA, - PSOutCustomCodeCbk customCodeCbkA, - void *customCodeCbkDataA, - GBool honorUserUnitA) { - underlayCbk = NULL; - underlayCbkData = NULL; - overlayCbk = NULL; - overlayCbkData = NULL; - customCodeCbk = customCodeCbkA; - customCodeCbkData = customCodeCbkDataA; - - rasterizePage = NULL; - fontInfo = new GList(); - fontFileInfo = new GHash(); - imgIDs = NULL; - formIDs = NULL; - visitedResources = NULL; - saveStack = NULL; - paperSizes = NULL; - embFontList = NULL; - customColors = NULL; - haveTextClip = gFalse; - t3String = NULL; - - init(outputFuncA, outputStreamA, psGeneric, - docA, firstPageA, lastPageA, modeA, - imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, honorUserUnitA); -} - -void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, - PSFileType fileTypeA, PDFDoc *docA, - int firstPageA, int lastPageA, PSOutMode modeA, - int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, - GBool manualCtrlA, GBool honorUserUnitA) { - Catalog *catalog; - Page *page; - PDFRectangle *box; - PSOutPaperSize *size; - PSFontFileInfo *ff; - GList *names; - double userUnit; - int pg, w, h, i; - - // initialize - ok = gTrue; - outputFunc = outputFuncA; - outputStream = outputStreamA; - fileType = fileTypeA; - doc = docA; - xref = doc->getXRef(); - catalog = doc->getCatalog(); - if ((firstPage = firstPageA) < 1) { - firstPage = 1; - } - if ((lastPage = lastPageA) > doc->getNumPages()) { - lastPage = doc->getNumPages(); - } - level = globalParams->getPSLevel(); - mode = modeA; - honorUserUnit = honorUserUnitA; - paperWidth = globalParams->getPSPaperWidth(); - paperHeight = globalParams->getPSPaperHeight(); - imgLLX = imgLLXA; - imgLLY = imgLLYA; - imgURX = imgURXA; - imgURY = imgURYA; - if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) { - globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY); - } - if (paperWidth < 0 || paperHeight < 0) { - paperMatch = gTrue; - paperSizes = new GList(); - paperWidth = paperHeight = 1; // in case the document has zero pages - for (pg = firstPage; pg <= lastPage; ++pg) { - page = catalog->getPage(pg); - if (honorUserUnit) { - userUnit = page->getUserUnit(); - } else { - userUnit = 1; - } - if (globalParams->getPSUseCropBoxAsPage()) { - w = (int)ceil(page->getCropWidth() * userUnit); - h = (int)ceil(page->getCropHeight() * userUnit); - } else { - w = (int)ceil(page->getMediaWidth() * userUnit); - h = (int)ceil(page->getMediaHeight() * userUnit); - } - for (i = 0; i < paperSizes->getLength(); ++i) { - size = (PSOutPaperSize *)paperSizes->get(i); - if (size->w == w && size->h == h) { - break; - } - } - if (i == paperSizes->getLength()) { - paperSizes->append(new PSOutPaperSize(w, h)); - } - if (w > paperWidth) { - paperWidth = w; - } - if (h > paperHeight) { - paperHeight = h; - } - } - // NB: img{LLX,LLY,URX,URY} will be set by startPage() - } else { - paperMatch = gFalse; - } - preload = globalParams->getPSPreload(); - manualCtrl = manualCtrlA; - if (mode == psModeForm) { - lastPage = firstPage; - } - processColors = 0; - inType3Char = gFalse; - -#if OPI_SUPPORT - // initialize OPI nesting levels - opi13Nest = 0; - opi20Nest = 0; -#endif - - tx0 = ty0 = -1; - xScale0 = yScale0 = 0; - rotate0 = -1; - clipLLX0 = clipLLY0 = 0; - clipURX0 = clipURY0 = -1; - - // initialize font lists, etc. - for (i = 0; i < 14; ++i) { - ff = new PSFontFileInfo(new GString(psBase14SubstFonts[i].psName), - fontType1, psFontFileResident); - fontFileInfo->add(ff->psName, ff); - } - names = globalParams->getPSResidentFonts(); - for (i = 0; i < names->getLength(); ++i) { - if (!fontFileInfo->lookup((GString *)names->get(i))) { - ff = new PSFontFileInfo((GString *)names->get(i), fontType1, - psFontFileResident); - fontFileInfo->add(ff->psName, ff); - } - } - delete names; - imgIDLen = 0; - imgIDSize = 0; - formIDLen = 0; - formIDSize = 0; - - noStateChanges = gFalse; - saveStack = new GList(); - numTilingPatterns = 0; - nextFunc = 0; - - // initialize embedded font resource comment list - embFontList = new GString(); - - if (!manualCtrl) { - // this check is needed in case the document has zero pages - if (firstPage <= catalog->getNumPages()) { - writeHeader(catalog->getPage(firstPage)->getMediaBox(), - catalog->getPage(firstPage)->getCropBox(), - catalog->getPage(firstPage)->getRotate()); - } else { - box = new PDFRectangle(0, 0, 1, 1); - writeHeader(box, box, 0); - delete box; - } - if (mode != psModeForm) { - writePS("%%BeginProlog\n"); - } - writeXpdfProcset(); - if (mode != psModeForm) { - writePS("%%EndProlog\n"); - writePS("%%BeginSetup\n"); - } - writeDocSetup(catalog); - if (mode != psModeForm) { - writePS("%%EndSetup\n"); - } - } - - // initialize sequential page number - seqPage = 1; -} - -PSOutputDev::~PSOutputDev() { - PSOutCustomColor *cc; - - if (ok) { - if (!manualCtrl) { - writePS("%%Trailer\n"); - writeTrailer(); - if (mode != psModeForm) { - writePS("%%EOF\n"); - } - } - if (fileType == psFile) { - fclose((FILE *)outputStream); - } -#ifdef HAVE_POPEN - else if (fileType == psPipe) { - pclose((FILE *)outputStream); -#ifndef _WIN32 - signal(SIGPIPE, (SignalFunc)SIG_DFL); -#endif - } -#endif - } - gfree(rasterizePage); - if (paperSizes) { - deleteGList(paperSizes, PSOutPaperSize); - } - if (embFontList) { - delete embFontList; - } - deleteGList(fontInfo, PSFontInfo); - deleteGHash(fontFileInfo, PSFontFileInfo); - gfree(imgIDs); - gfree(formIDs); - if (saveStack) { - delete saveStack; - } - while (customColors) { - cc = customColors; - customColors = cc->next; - delete cc; - } -} - -GBool PSOutputDev::checkIO() { - if (fileType == psFile || fileType == psPipe || fileType == psStdout) { - if (ferror((FILE *)outputStream)) { - error(errIO, -1, "Error writing to PostScript file"); - return gFalse; - } - } - return gTrue; -} - -void PSOutputDev::writeHeader(PDFRectangle *mediaBox, PDFRectangle *cropBox, - int pageRotate) { - Object info, obj1; - PSOutPaperSize *size; - double x1, y1, x2, y2; - int i; - - switch (mode) { - case psModePS: - writePS("%!PS-Adobe-3.0\n"); - break; - case psModeEPS: - writePS("%!PS-Adobe-3.0 EPSF-3.0\n"); - break; - case psModeForm: - writePS("%!PS-Adobe-3.0 Resource-Form\n"); - break; - } - - writePSFmt("%XpdfVersion: {0:s}\n", xpdfVersion); - xref->getDocInfo(&info); - if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) { - writePS("%%Creator: "); - writePSTextLine(obj1.getString()); - } - obj1.free(); - if (info.isDict() && info.dictLookup("Title", &obj1)->isString()) { - writePS("%%Title: "); - writePSTextLine(obj1.getString()); - } - obj1.free(); - info.free(); - writePSFmt("%%LanguageLevel: {0:d}\n", - level >= psLevel3 ? 3 : level >= psLevel2 ? 2 : 1); - if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { - writePS("%%DocumentProcessColors: (atend)\n"); - writePS("%%DocumentCustomColors: (atend)\n"); - } - writePS("%%DocumentSuppliedResources: (atend)\n"); - - switch (mode) { - case psModePS: - if (paperMatch) { - for (i = 0; i < paperSizes->getLength(); ++i) { - size = (PSOutPaperSize *)paperSizes->get(i); - writePSFmt("%%{0:s} {1:d}x{2:d} {1:d} {2:d} 0 () ()\n", - i==0 ? "DocumentMedia:" : "+", size->w, size->h); - } - } else { - writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n", - paperWidth, paperHeight); - } - writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight); - writePSFmt("%%Pages: {0:d}\n", lastPage - firstPage + 1); - writePS("%%EndComments\n"); - if (!paperMatch) { - writePS("%%BeginDefaults\n"); - writePS("%%PageMedia: plain\n"); - writePS("%%EndDefaults\n"); - } - break; - case psModeEPS: - epsX1 = cropBox->x1; - epsY1 = cropBox->y1; - epsX2 = cropBox->x2; - epsY2 = cropBox->y2; - if (pageRotate == 0 || pageRotate == 180) { - x1 = epsX1; - y1 = epsY1; - x2 = epsX2; - y2 = epsY2; - } else { // pageRotate == 90 || pageRotate == 270 - x1 = 0; - y1 = 0; - x2 = epsY2 - epsY1; - y2 = epsX2 - epsX1; - } - writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n", - (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2)); - if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) || - floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) { - writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", - x1, y1, x2, y2); - } - writePS("%%EndComments\n"); - break; - case psModeForm: - writePS("%%EndComments\n"); - writePS("32 dict dup begin\n"); - writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n", - (int)floor(mediaBox->x1), (int)floor(mediaBox->y1), - (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2)); - writePS("/FormType 1 def\n"); - writePS("/Matrix [1 0 0 1 0 0] def\n"); - break; - } -} - -void PSOutputDev::writeXpdfProcset() { - GBool lev1, lev2, lev3, nonSep, gray, sep; - const char **p; - const char *q; - double w; - - writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion); - writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright); - lev1 = lev2 = lev3 = nonSep = gray = sep = gTrue; - for (p = prolog; *p; ++p) { - if ((*p)[0] == '~') { - lev1 = lev2 = lev3 = nonSep = gray = sep = gFalse; - for (q = *p + 1; *q; ++q) { - switch (*q) { - case '1': lev1 = gTrue; break; - case '2': lev2 = gTrue; break; - case '3': lev3 = gTrue; break; - case 'g': gray = gTrue; break; - case 'n': nonSep = gTrue; break; - case 's': sep = gTrue; break; - } - } - } else if ((level == psLevel1 && lev1 && nonSep) || - (level == psLevel1Sep && lev1 && sep) || - (level == psLevel2 && lev2 && nonSep) || - (level == psLevel2Gray && lev2 && gray) || - (level == psLevel2Sep && lev2 && sep) || - (level == psLevel3 && lev3 && nonSep) || - (level == psLevel3Gray && lev3 && gray) || - (level == psLevel3Sep && lev3 && sep)) { - writePSFmt("{0:s}\n", *p); - } - } - if ((w = globalParams->getPSMinLineWidth()) > 0) { - writePSFmt("/pdfMinLineWidth {0:.4g} def\n", w); - for (p = minLineWidthProlog; *p; ++p) { - writePSFmt("{0:s}\n", *p); - } - } - writePS("%%EndResource\n"); - - if (level >= psLevel3) { - for (p = cmapProlog; *p; ++p) { - writePSFmt("{0:s}\n", *p); - } - } -} - -void PSOutputDev::writeDocSetup(Catalog *catalog) { - Page *page; - Dict *resDict; - Annots *annots; - Form *form; - Object obj1, obj2, obj3; - GString *s; - GBool needDefaultFont; - int pg, i, j; - - // check to see which pages will be rasterized - if (firstPage <= lastPage) { - rasterizePage = (char *)gmalloc(lastPage - firstPage + 1); - for (pg = firstPage; pg <= lastPage; ++pg) { - rasterizePage[pg - firstPage] = (char)checkIfPageNeedsToBeRasterized(pg); - } - } else { - rasterizePage = NULL; - } - - visitedResources = (char *)gmalloc(xref->getNumObjects()); - memset(visitedResources, 0, xref->getNumObjects()); - - if (mode == psModeForm) { - // swap the form and xpdf dicts - writePS("xpdf end begin dup begin\n"); - } else { - writePS("xpdf begin\n"); - } - needDefaultFont = gFalse; - for (pg = firstPage; pg <= lastPage; ++pg) { - if (rasterizePage[pg - firstPage]) { - continue; - } - page = catalog->getPage(pg); - if ((resDict = page->getResourceDict())) { - setupResources(resDict); - } - annots = new Annots(doc, page->getAnnots(&obj1)); - obj1.free(); - if (annots->getNumAnnots()) { - needDefaultFont = gTrue; - } - for (i = 0; i < annots->getNumAnnots(); ++i) { - if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) { - obj1.streamGetDict()->lookup("Resources", &obj2); - if (obj2.isDict()) { - setupResources(obj2.getDict()); - } - obj2.free(); - } - obj1.free(); - } - delete annots; - } - if ((form = catalog->getForm())) { - if (form->getNumFields() > 0) { - needDefaultFont = gTrue; - } - for (i = 0; i < form->getNumFields(); ++i) { - form->getField(i)->getResources(&obj1); - if (obj1.isArray()) { - for (j = 0; j < obj1.arrayGetLength(); ++j) { - obj1.arrayGet(j, &obj2); - if (obj2.isDict()) { - setupResources(obj2.getDict()); - } - obj2.free(); - } - } else if (obj1.isDict()) { - setupResources(obj1.getDict()); - } - obj1.free(); - } - } - if (needDefaultFont) { - setupDefaultFont(); - } - if (mode != psModeForm) { - if (mode != psModeEPS && !manualCtrl) { - writePSFmt("{0:s} pdfSetup\n", - globalParams->getPSDuplex() ? "true" : "false"); - if (!paperMatch) { - writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight); - } - } -#if OPI_SUPPORT - if (globalParams->getPSOPI()) { - writePS("/opiMatrix matrix currentmatrix def\n"); - } -#endif - } - if (customCodeCbk) { - if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0, - customCodeCbkData))) { - writePS(s->getCString()); - delete s; - } - } - if (mode != psModeForm) { - writePS("end\n"); - } - - gfree(visitedResources); - visitedResources = NULL; -} - -void PSOutputDev::writePageTrailer() { - if (mode != psModeForm) { - writePS("pdfEndPage\n"); - } -} - -void PSOutputDev::writeTrailer() { - PSOutCustomColor *cc; - - if (mode == psModeForm) { - writePS("/Foo exch /Form defineresource pop\n"); - } else { - writePS("%%DocumentSuppliedResources:\n"); - writePS(embFontList->getCString()); - if (level == psLevel1Sep || level == psLevel2Sep || - level == psLevel3Sep) { - writePS("%%DocumentProcessColors:"); - if (processColors & psProcessCyan) { - writePS(" Cyan"); - } - if (processColors & psProcessMagenta) { - writePS(" Magenta"); - } - if (processColors & psProcessYellow) { - writePS(" Yellow"); - } - if (processColors & psProcessBlack) { - writePS(" Black"); - } - writePS("\n"); - writePS("%%DocumentCustomColors:"); - for (cc = customColors; cc; cc = cc->next) { - writePS(" "); - writePSString(cc->name); - } - writePS("\n"); - writePS("%%CMYKCustomColor:\n"); - for (cc = customColors; cc; cc = cc->next) { - writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", - cc->c, cc->m, cc->y, cc->k); - writePSString(cc->name); - writePS("\n"); - } - } - } -} - -GBool PSOutputDev::checkIfPageNeedsToBeRasterized(int pg) { - PreScanOutputDev *scan; - GBool rasterize; - - if (globalParams->getPSAlwaysRasterize()) { - rasterize = gTrue; - } else { - scan = new PreScanOutputDev(); - //~ this could depend on the printing flag, e.g., if an annotation - //~ uses transparency --> need to pass the printing flag into - //~ constructor, init, writeDocSetup - doc->getCatalog()->getPage(pg)->display(scan, 72, 72, 0, - gTrue, gTrue, gTrue); - rasterize = scan->usesTransparency() || scan->usesPatternImageMask(); - delete scan; - if (rasterize && globalParams->getPSNeverRasterize()) { - error(errSyntaxWarning, -1, - "PDF page uses transparency and the psNeverRasterize option is " - "set - output may not be correct"); - rasterize = gFalse; - } - } - return rasterize; -} - -void PSOutputDev::setupResources(Dict *resDict) { - Object xObjDict, xObjRef, xObj, patDict, patRef, pat; - Object gsDict, gsRef, gs, smask, smaskGroup, resObj; - Ref ref0; - GBool skip; - int i; - - setupFonts(resDict); - setupImages(resDict); - - //----- recursively scan XObjects - resDict->lookup("XObject", &xObjDict); - if (xObjDict.isDict()) { - for (i = 0; i < xObjDict.dictGetLength(); ++i) { - - // check for an already-visited XObject - skip = gFalse; - if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) { - ref0 = xObjRef.getRef(); - if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) { - skip = gTrue; - } else { - skip = (GBool)visitedResources[ref0.num]; - visitedResources[ref0.num] = 1; - } - } - if (!skip) { - - // process the XObject's resource dictionary - xObjDict.dictGetVal(i, &xObj); - if (xObj.isStream()) { - xObj.streamGetDict()->lookup("Resources", &resObj); - if (resObj.isDict()) { - setupResources(resObj.getDict()); - } - resObj.free(); - } - xObj.free(); - } - - xObjRef.free(); - } - } - xObjDict.free(); - - //----- recursively scan Patterns - resDict->lookup("Pattern", &patDict); - if (patDict.isDict()) { - inType3Char = gTrue; - for (i = 0; i < patDict.dictGetLength(); ++i) { - - // check for an already-visited Pattern - skip = gFalse; - if ((patDict.dictGetValNF(i, &patRef)->isRef())) { - ref0 = patRef.getRef(); - if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) { - skip = gTrue; - } else { - skip = (GBool)visitedResources[ref0.num]; - visitedResources[ref0.num] = 1; - } - } - if (!skip) { - - // process the Pattern's resource dictionary - patDict.dictGetVal(i, &pat); - if (pat.isStream()) { - pat.streamGetDict()->lookup("Resources", &resObj); - if (resObj.isDict()) { - setupResources(resObj.getDict()); - } - resObj.free(); - } - pat.free(); - } - - patRef.free(); - } - inType3Char = gFalse; - } - patDict.free(); - - //----- recursively scan SMask transparency groups in ExtGState dicts - resDict->lookup("ExtGState", &gsDict); - if (gsDict.isDict()) { - for (i = 0; i < gsDict.dictGetLength(); ++i) { - - // check for an already-visited ExtGState - skip = gFalse; - if ((gsDict.dictGetValNF(i, &gsRef)->isRef())) { - ref0 = gsRef.getRef(); - if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) { - skip = gTrue; - } else { - skip = (GBool)visitedResources[ref0.num]; - visitedResources[ref0.num] = 1; - } - } - if (!skip) { - - // process the ExtGState's SMask's transparency group's resource dict - if (gsDict.dictGetVal(i, &gs)->isDict()) { - if (gs.dictLookup("SMask", &smask)->isDict()) { - if (smask.dictLookup("G", &smaskGroup)->isStream()) { - smaskGroup.streamGetDict()->lookup("Resources", &resObj); - if (resObj.isDict()) { - setupResources(resObj.getDict()); - } - resObj.free(); - } - smaskGroup.free(); - } - smask.free(); - } - gs.free(); - } - - gsRef.free(); - } - } - gsDict.free(); - - setupForms(resDict); -} - -void PSOutputDev::setupFonts(Dict *resDict) { - Object obj1, obj2; - Ref r; - GfxFontDict *gfxFontDict; - GfxFont *font; - int i; - - gfxFontDict = NULL; - resDict->lookupNF("Font", &obj1); - if (obj1.isRef()) { - obj1.fetch(xref, &obj2); - if (obj2.isDict()) { - r = obj1.getRef(); - gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict()); - } - obj2.free(); - } else if (obj1.isDict()) { - gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict()); - } - if (gfxFontDict) { - for (i = 0; i < gfxFontDict->getNumFonts(); ++i) { - if ((font = gfxFontDict->getFont(i))) { - setupFont(font, resDict); - } - } - delete gfxFontDict; - } - obj1.free(); -} - -void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { - PSFontInfo *fi; - GfxFontLoc *fontLoc; - GBool subst; - char buf[16]; - UnicodeMap *uMap; - char *charName; - double xs, ys; - int code; - double w1, w2; - int i, j; - - // check if font is already set up - for (i = 0; i < fontInfo->getLength(); ++i) { - fi = (PSFontInfo *)fontInfo->get(i); - if (fi->fontID.num == font->getID()->num && - fi->fontID.gen == font->getID()->gen) { - return; - } - } - - // add fontInfo entry - fi = new PSFontInfo(*font->getID()); - fontInfo->append(fi); - - xs = ys = 1; - subst = gFalse; - - if (font->getType() == fontType3) { - fi->ff = setupType3Font(font, parentResDict); - } else { - if ((fontLoc = font->locateFont(xref, gTrue))) { - switch (fontLoc->locType) { - case gfxFontLocEmbedded: - switch (fontLoc->fontType) { - case fontType1: - fi->ff = setupEmbeddedType1Font(font, &fontLoc->embFontID); - break; - case fontType1C: - fi->ff = setupEmbeddedType1CFont(font, &fontLoc->embFontID); - break; - case fontType1COT: - fi->ff = setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID); - break; - case fontTrueType: - case fontTrueTypeOT: - fi->ff = setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID); - break; - case fontCIDType0C: - fi->ff = setupEmbeddedCIDType0Font(font, &fontLoc->embFontID); - break; - case fontCIDType2: - case fontCIDType2OT: - //~ should check to see if font actually uses vertical mode - fi->ff = setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, - gTrue); - break; - case fontCIDType0COT: - fi->ff = setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID); - break; - default: - break; - } - break; - case gfxFontLocExternal: - //~ add cases for other external 16-bit fonts - switch (fontLoc->fontType) { - case fontType1: - fi->ff = setupExternalType1Font(font, fontLoc->path); - break; - case fontTrueType: - case fontTrueTypeOT: - fi->ff = setupExternalTrueTypeFont(font, fontLoc->path, - fontLoc->fontNum); - break; - case fontCIDType2: - case fontCIDType2OT: - //~ should check to see if font actually uses vertical mode - fi->ff = setupExternalCIDTrueTypeFont(font, fontLoc->path, - fontLoc->fontNum, gTrue); - break; - case fontCIDType0COT: - fi->ff = setupExternalOpenTypeCFFFont(font, fontLoc->path); - break; - default: - break; - } - break; - case gfxFontLocResident: - if (!(fi->ff = (PSFontFileInfo *)fontFileInfo->lookup(fontLoc->path))) { - // handle psFontPassthrough - fi->ff = new PSFontFileInfo(fontLoc->path->copy(), fontLoc->fontType, - psFontFileResident); - fontFileInfo->add(fi->ff->psName, fi->ff); - } - break; - } - } - - if (!fi->ff) { - if (font->isCIDFont()) { - error(errSyntaxError, -1, - "Couldn't find a font for '{0:s}' ('{1:s}' character collection)", - font->getName() ? font->getName()->getCString() - : "(unnamed)", - ((GfxCIDFont *)font)->getCollection() - ? ((GfxCIDFont *)font)->getCollection()->getCString() - : "(unknown)"); - } else { - error(errSyntaxError, -1, - "Couldn't find a font for '{0:s}'", - font->getName() ? font->getName()->getCString() - : "(unnamed)"); - } - delete fontLoc; - return; - } - - // scale substituted 8-bit fonts - if (fontLoc->locType == gfxFontLocResident && - fontLoc->substIdx >= 0) { - subst = gTrue; - for (code = 0; code < 256; ++code) { - if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && - charName[0] == 'm' && charName[1] == '\0') { - break; - } - } - if (code < 256) { - w1 = ((Gfx8BitFont *)font)->getWidth((Guchar)code); - } else { - w1 = 0; - } - w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth; - xs = w1 / w2; - if (xs < 0.1) { - xs = 1; - } - } - - // handle encodings for substituted CID fonts - if (fontLoc->locType == gfxFontLocResident && - fontLoc->fontType >= fontCIDType0) { - subst = gTrue; - if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) { - fi->ff->encoding = fontLoc->encoding->copy(); - uMap->decRefCnt(); - } else { - error(errSyntaxError, -1, - "Couldn't find Unicode map for 16-bit font encoding '{0:t}'", - fontLoc->encoding); - } - } - - delete fontLoc; - } - - // generate PostScript code to set up the font - if (font->isCIDFont()) { - if (level >= psLevel3) { - writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n", - font->getID()->num, font->getID()->gen, fi->ff->psName, - font->getWMode()); - } else { - writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n", - font->getID()->num, font->getID()->gen, fi->ff->psName, - font->getWMode()); - } - } else { - writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n", - font->getID()->num, font->getID()->gen, fi->ff->psName, xs, ys); - for (i = 0; i < 256; i += 8) { - writePS((char *)((i == 0) ? "[ " : " ")); - for (j = 0; j < 8; ++j) { - if (font->getType() == fontTrueType && - !subst && - !((Gfx8BitFont *)font)->getHasEncoding()) { - sprintf(buf, "c%02x", i+j); - charName = buf; - } else { - charName = ((Gfx8BitFont *)font)->getCharName(i+j); - } - writePS("/"); - writePSName(charName ? charName : (char *)".notdef"); - // the empty name is legal in PDF and PostScript, but PostScript - // uses a double-slash (//...) for "immediately evaluated names", - // so we need to add a space character here - if (charName && !charName[0]) { - writePS(" "); - } - } - writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n"); - } - writePS("pdfMakeFont\n"); - } -} - -PSFontFileInfo *PSOutputDev::setupEmbeddedType1Font(GfxFont *font, Ref *id) { - GString *psName, *origFont, *cleanFont; - PSFontFileInfo *ff; - Object refObj, strObj, obj1, obj2; - Dict *dict; - char buf[4096]; - GBool rename; - int length1, length2, n; - - // check if font is already embedded - if (!font->getEmbeddedFontName()) { - rename = gTrue; - } else if ((ff = (PSFontFileInfo *) - fontFileInfo->lookup(font->getEmbeddedFontName()))) { - if (ff->loc == psFontFileEmbedded && - ff->embFontID.num == id->num && - ff->embFontID.gen == id->gen) { - return ff; - } - rename = gTrue; - } else { - rename = gFalse; - } - - // generate name - // (this assumes that the PS font name matches the PDF font name) - if (rename) { - psName = makePSFontName(font, id); - } else { - psName = font->getEmbeddedFontName()->copy(); - } - - // get the font stream and info - refObj.initRef(id->num, id->gen); - refObj.fetch(xref, &strObj); - refObj.free(); - if (!strObj.isStream()) { - error(errSyntaxError, -1, "Embedded font file object is not a stream"); - goto err1; - } - if (!(dict = strObj.streamGetDict())) { - error(errSyntaxError, -1, - "Embedded font stream is missing its dictionary"); - goto err1; - } - dict->lookup("Length1", &obj1); - dict->lookup("Length2", &obj2); - if (!obj1.isInt() || !obj2.isInt()) { - error(errSyntaxError, -1, - "Missing length fields in embedded font stream dictionary"); - obj1.free(); - obj2.free(); - goto err1; - } - length1 = obj1.getInt(); - length2 = obj2.getInt(); - obj1.free(); - obj2.free(); - - // read the font file - origFont = new GString(); - strObj.streamReset(); - while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) { - origFont->append(buf, n); - } - strObj.streamClose(); - strObj.free(); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // clean up the font file - cleanFont = fixType1Font(origFont, length1, length2); - if (rename) { - renameType1Font(cleanFont, psName); - } - writePSBlock(cleanFont->getCString(), cleanFont->getLength()); - delete cleanFont; - delete origFont; - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); - ff->embFontID = *id; - fontFileInfo->add(ff->psName, ff); - return ff; - - err1: - strObj.free(); - delete psName; - return NULL; -} - -PSFontFileInfo *PSOutputDev::setupExternalType1Font(GfxFont *font, - GString *fileName) { - static char hexChar[17] = "0123456789abcdef"; - GString *psName; - PSFontFileInfo *ff; - FILE *fontFile; - int buf[6]; - int c, n, i; - - if (font->getName()) { - // check if font is already embedded - if ((ff = (PSFontFileInfo *)fontFileInfo->lookup(font->getName()))) { - return ff; - } - // this assumes that the PS font name matches the PDF font name - psName = font->getName()->copy(); - } else { - // generate name - //~ this won't work -- the PS font name won't match - psName = makePSFontName(font, font->getID()); - } - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // open the font file - if (!(fontFile = fopen(fileName->getCString(), "rb"))) { - error(errIO, -1, "Couldn't open external font file"); - return NULL; - } - - // check for PFB format - buf[0] = fgetc(fontFile); - buf[1] = fgetc(fontFile); - if (buf[0] == 0x80 && buf[1] == 0x01) { - while (1) { - for (i = 2; i < 6; ++i) { - buf[i] = fgetc(fontFile); - } - if (buf[2] == EOF || buf[3] == EOF || buf[4] == EOF || buf[5] == EOF) { - break; - } - n = buf[2] + (buf[3] << 8) + (buf[4] << 16) + (buf[5] << 24); - if (buf[1] == 0x01) { - for (i = 0; i < n; ++i) { - if ((c = fgetc(fontFile)) == EOF) { - break; - } - writePSChar((char)c); - } - } else { - for (i = 0; i < n; ++i) { - if ((c = fgetc(fontFile)) == EOF) { - break; - } - writePSChar(hexChar[(c >> 4) & 0x0f]); - writePSChar(hexChar[c & 0x0f]); - if (i % 32 == 31) { - writePSChar('\n'); - } - } - } - buf[0] = fgetc(fontFile); - buf[1] = fgetc(fontFile); - if (buf[0] == EOF || buf[1] == EOF || - (buf[0] == 0x80 && buf[1] == 0x03)) { - break; - } else if (!(buf[0] == 0x80 && - (buf[1] == 0x01 || buf[1] == 0x02))) { - error(errSyntaxError, -1, - "Invalid PFB header in external font file"); - break; - } - } - writePSChar('\n'); - - // plain text (PFA) format - } else { - writePSChar((char)buf[0]); - writePSChar((char)buf[1]); - while ((c = fgetc(fontFile)) != EOF) { - writePSChar((char)c); - } - } - - fclose(fontFile); - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); - ff->extFileName = fileName->copy(); - fontFileInfo->add(ff->psName, ff); - return ff; -} - -PSFontFileInfo *PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) { - GString *psName; - PSFontFileInfo *ff; - char *fontBuf; - int fontLen; - FoFiType1C *ffT1C; - GHashIter *iter; - - // check if font is already embedded - fontFileInfo->startIter(&iter); - while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { - if (ff->loc == psFontFileEmbedded && - ff->embFontID.num == id->num && - ff->embFontID.gen == id->gen) { - fontFileInfo->killIter(&iter); - return ff; - } - } - - // generate name - psName = makePSFontName(font, id); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // convert it to a Type 1 font - if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { - if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { - ffT1C->convertToType1(psName->getCString(), NULL, gTrue, - outputFunc, outputStream); - delete ffT1C; - } - gfree(fontBuf); - } - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); - ff->embFontID = *id; - fontFileInfo->add(ff->psName, ff); - return ff; -} - -PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, - Ref *id) { - GString *psName; - PSFontFileInfo *ff; - char *fontBuf; - int fontLen; - FoFiTrueType *ffTT; - GHashIter *iter; - - // check if font is already embedded - fontFileInfo->startIter(&iter); - while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { - if (ff->loc == psFontFileEmbedded && - ff->embFontID.num == id->num && - ff->embFontID.gen == id->gen) { - fontFileInfo->killIter(&iter); - return ff; - } - } - - // generate name - psName = makePSFontName(font, id); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // convert it to a Type 1 font - if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { - if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0, gTrue))) { - if (ffTT->isOpenTypeCFF()) { - ffTT->convertToType1(psName->getCString(), NULL, gTrue, - outputFunc, outputStream); - } - delete ffTT; - } - gfree(fontBuf); - } - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); - ff->embFontID = *id; - fontFileInfo->add(ff->psName, ff); - return ff; -} - -PSFontFileInfo *PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id) { - GString *psName; - PSFontFileInfo *ff; - char *fontBuf; - int fontLen; - FoFiTrueType *ffTT; - int *codeToGID; - GHashIter *iter; - - // get the code-to-GID mapping - if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) { - return NULL; - } - if (!(ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) { - gfree(fontBuf); - return NULL; - } - codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); - - // check if font is already embedded - fontFileInfo->startIter(&iter); - while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { - if (ff->loc == psFontFileEmbedded && - ff->type == font->getType() && - ff->embFontID.num == id->num && - ff->embFontID.gen == id->gen && - ff->codeToGIDLen == 256 && - !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) { - fontFileInfo->killIter(&iter); - gfree(codeToGID); - delete ffTT; - gfree(fontBuf); - return ff; - } - } - - // generate name - psName = makePSFontName(font, id); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // convert it to a Type 42 font - ffTT->convertToType42(psName->getCString(), - ((Gfx8BitFont *)font)->getHasEncoding() - ? ((Gfx8BitFont *)font)->getEncoding() - : (char **)NULL, - codeToGID, outputFunc, outputStream); - delete ffTT; - gfree(fontBuf); - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); - ff->embFontID = *id; - ff->codeToGID = codeToGID; - ff->codeToGIDLen = 256; - fontFileInfo->add(ff->psName, ff); - return ff; -} - -PSFontFileInfo *PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, - GString *fileName, - int fontNum) { - GString *psName; - PSFontFileInfo *ff; - FoFiTrueType *ffTT; - int *codeToGID; - GHashIter *iter; - - // get the code-to-GID mapping - if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) { - return NULL; - } - codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); - - // check if font is already embedded - fontFileInfo->startIter(&iter); - while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { - if (ff->loc == psFontFileExternal && - ff->type == font->getType() && - !ff->extFileName->cmp(fileName) && - ff->codeToGIDLen == 256 && - !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) { - fontFileInfo->killIter(&iter); - gfree(codeToGID); - delete ffTT; - return ff; - } - } - - // generate name - psName = makePSFontName(font, font->getID()); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // convert it to a Type 42 font - ffTT->convertToType42(psName->getCString(), - ((Gfx8BitFont *)font)->getHasEncoding() - ? ((Gfx8BitFont *)font)->getEncoding() - : (char **)NULL, - codeToGID, outputFunc, outputStream); - delete ffTT; - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); - ff->extFileName = fileName->copy(); - ff->codeToGID = codeToGID; - ff->codeToGIDLen = 256; - fontFileInfo->add(ff->psName, ff); - return ff; -} - -PSFontFileInfo *PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id) { - GString *psName; - PSFontFileInfo *ff; - char *fontBuf; - int fontLen; - FoFiType1C *ffT1C; - GHashIter *iter; - - // check if font is already embedded - fontFileInfo->startIter(&iter); - while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { - if (ff->loc == psFontFileEmbedded && - ff->embFontID.num == id->num && - ff->embFontID.gen == id->gen) { - fontFileInfo->killIter(&iter); - return ff; - } - } - - // generate name - psName = makePSFontName(font, id); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // convert it to a Type 0 font - if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { - if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { - if (globalParams->getPSLevel() >= psLevel3) { - // Level 3: use a CID font - ffT1C->convertToCIDType0(psName->getCString(), - ((GfxCIDFont *)font)->getCIDToGID(), - ((GfxCIDFont *)font)->getCIDToGIDLen(), - outputFunc, outputStream); - } else { - // otherwise: use a non-CID composite font - ffT1C->convertToType0(psName->getCString(), - ((GfxCIDFont *)font)->getCIDToGID(), - ((GfxCIDFont *)font)->getCIDToGIDLen(), - outputFunc, outputStream); - } - delete ffT1C; - } - gfree(fontBuf); - } - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); - ff->embFontID = *id; - fontFileInfo->add(ff->psName, ff); - return ff; -} - -PSFontFileInfo *PSOutputDev::setupEmbeddedCIDTrueTypeFont( - GfxFont *font, Ref *id, - GBool needVerticalMetrics) { - GString *psName; - PSFontFileInfo *ff; - char *fontBuf; - int fontLen; - FoFiTrueType *ffTT; - int *codeToGID; - int codeToGIDLen; - GHashIter *iter; - - // get the code-to-GID mapping - codeToGID = ((GfxCIDFont *)font)->getCIDToGID(); - codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen(); - - // check if font is already embedded - fontFileInfo->startIter(&iter); - while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { - if (ff->loc == psFontFileEmbedded && - ff->type == font->getType() && - ff->embFontID.num == id->num && - ff->embFontID.gen == id->gen && - ff->codeToGIDLen == codeToGIDLen && - ((!ff->codeToGID && !codeToGID) || - (ff->codeToGID && codeToGID && - !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))))) { - fontFileInfo->killIter(&iter); - return ff; - } - } - - // generate name - psName = makePSFontName(font, id); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // convert it to a Type 0 font - if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { - if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) { - if (globalParams->getPSLevel() >= psLevel3) { - // Level 3: use a CID font - ffTT->convertToCIDType2(psName->getCString(), - codeToGID, codeToGIDLen, - needVerticalMetrics, - outputFunc, outputStream); - } else { - // otherwise: use a non-CID composite font - ffTT->convertToType0(psName->getCString(), - codeToGID, codeToGIDLen, - needVerticalMetrics, - outputFunc, outputStream); - } - delete ffTT; - } - gfree(fontBuf); - } - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); - ff->embFontID = *id; - if (codeToGIDLen) { - ff->codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); - memcpy(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int)); - ff->codeToGIDLen = codeToGIDLen; - } - fontFileInfo->add(ff->psName, ff); - return ff; -} - -PSFontFileInfo *PSOutputDev::setupExternalCIDTrueTypeFont( - GfxFont *font, - GString *fileName, - int fontNum, - GBool needVerticalMetrics) { - GString *psName; - PSFontFileInfo *ff; - FoFiTrueType *ffTT; - int *codeToGID; - int codeToGIDLen; - CharCodeToUnicode *ctu; - Unicode uBuf[8]; - int cmap, cmapPlatform, cmapEncoding, code; - GHashIter *iter; - - // create a code-to-GID mapping, via Unicode - if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) { - return NULL; - } - if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) { - error(errSyntaxError, -1, - "Couldn't find a mapping to Unicode for font '{0:s}'", - font->getName() ? font->getName()->getCString() : "(unnamed)"); - delete ffTT; - return NULL; - } - // look for a Unicode cmap - for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) { - cmapPlatform = ffTT->getCmapPlatform(cmap); - cmapEncoding = ffTT->getCmapEncoding(cmap); - if ((cmapPlatform == 3 && cmapEncoding == 1) || - (cmapPlatform == 0 && cmapEncoding <= 4)) { - break; - } - } - if (cmap >= ffTT->getNumCmaps()) { - error(errSyntaxError, -1, - "Couldn't find a Unicode cmap in font '{0:s}'", - font->getName() ? font->getName()->getCString() : "(unnamed)"); - ctu->decRefCnt(); - delete ffTT; - return NULL; - } - // map CID -> Unicode -> GID - if (ctu->isIdentity()) { - codeToGIDLen = 65536; - } else { - codeToGIDLen = ctu->getLength(); - } - codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); - for (code = 0; code < codeToGIDLen; ++code) { - if (ctu->mapToUnicode(code, uBuf, 8) > 0) { - codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]); - } else { - codeToGID[code] = 0; - } - } - ctu->decRefCnt(); - - // check if font is already embedded - fontFileInfo->startIter(&iter); - while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { - if (ff->loc == psFontFileExternal && - ff->type == font->getType() && - !ff->extFileName->cmp(fileName) && - ff->codeToGIDLen == codeToGIDLen && - ff->codeToGID && - !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) { - fontFileInfo->killIter(&iter); - gfree(codeToGID); - delete ffTT; - return ff; - } - } - - // check for embedding permission - if (ffTT->getEmbeddingRights() < 1) { - error(errSyntaxError, -1, - "TrueType font '{0:s}' does not allow embedding", - font->getName() ? font->getName()->getCString() : "(unnamed)"); - gfree(codeToGID); - delete ffTT; - return NULL; - } - - // generate name - psName = makePSFontName(font, font->getID()); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // convert it to a Type 0 font - //~ this should use fontNum to load the correct font - if (globalParams->getPSLevel() >= psLevel3) { - // Level 3: use a CID font - ffTT->convertToCIDType2(psName->getCString(), - codeToGID, codeToGIDLen, - needVerticalMetrics, - outputFunc, outputStream); - } else { - // otherwise: use a non-CID composite font - ffTT->convertToType0(psName->getCString(), - codeToGID, codeToGIDLen, - needVerticalMetrics, - outputFunc, outputStream); - } - delete ffTT; - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); - ff->extFileName = fileName->copy(); - ff->codeToGID = codeToGID; - ff->codeToGIDLen = codeToGIDLen; - fontFileInfo->add(ff->psName, ff); - return ff; -} - -PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, - Ref *id) { - GString *psName; - PSFontFileInfo *ff; - char *fontBuf; - int fontLen; - FoFiTrueType *ffTT; - GHashIter *iter; - int n; - - // check if font is already embedded - fontFileInfo->startIter(&iter); - while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { - if (ff->loc == psFontFileEmbedded && - ff->embFontID.num == id->num && - ff->embFontID.gen == id->gen) { - fontFileInfo->killIter(&iter); - return ff; - } - } - - // generate name - psName = makePSFontName(font, id); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // convert it to a Type 0 font - if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { - if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0, gTrue))) { - if (ffTT->isOpenTypeCFF()) { - if (globalParams->getPSLevel() >= psLevel3) { - // Level 3: use a CID font - ffTT->convertToCIDType0(psName->getCString(), - ((GfxCIDFont *)font)->getCIDToGID(), - ((GfxCIDFont *)font)->getCIDToGIDLen(), - outputFunc, outputStream); - } else { - // otherwise: use a non-CID composite font - ffTT->convertToType0(psName->getCString(), - ((GfxCIDFont *)font)->getCIDToGID(), - ((GfxCIDFont *)font)->getCIDToGIDLen(), - outputFunc, outputStream); - } - } - delete ffTT; - } - gfree(fontBuf); - } - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); - ff->embFontID = *id; - if ((n = ((GfxCIDFont *)font)->getCIDToGIDLen())) { - ff->codeToGID = (int *)gmallocn(n, sizeof(int)); - memcpy(ff->codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), n * sizeof(int)); - ff->codeToGIDLen = n; - } - fontFileInfo->add(ff->psName, ff); - return ff; -} - -// This assumes an OpenType CFF font that has a Unicode cmap (in the -// OpenType section), and a CFF blob that uses an identity CID-to-GID -// mapping. -PSFontFileInfo *PSOutputDev::setupExternalOpenTypeCFFFont(GfxFont *font, - GString *fileName) { - GString *psName; - PSFontFileInfo *ff; - FoFiTrueType *ffTT; - GHashIter *iter; - CharCodeToUnicode *ctu; - Unicode uBuf[8]; - int *codeToGID; - int codeToGIDLen; - int cmap, cmapPlatform, cmapEncoding, code; - - // create a code-to-GID mapping, via Unicode - if (!(ffTT = FoFiTrueType::load(fileName->getCString(), 0, gTrue))) { - return NULL; - } - if (!ffTT->isOpenTypeCFF()) { - delete ffTT; - return NULL; - } - if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) { - error(errSyntaxError, -1, - "Couldn't find a mapping to Unicode for font '{0:s}'", - font->getName() ? font->getName()->getCString() : "(unnamed)"); - delete ffTT; - return NULL; - } - // look for a Unicode cmap - for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) { - cmapPlatform = ffTT->getCmapPlatform(cmap); - cmapEncoding = ffTT->getCmapEncoding(cmap); - if ((cmapPlatform == 3 && cmapEncoding == 1) || - (cmapPlatform == 0 && cmapEncoding <= 4)) { - break; - } - } - if (cmap >= ffTT->getNumCmaps()) { - error(errSyntaxError, -1, - "Couldn't find a Unicode cmap in font '{0:s}'", - font->getName() ? font->getName()->getCString() : "(unnamed)"); - ctu->decRefCnt(); - delete ffTT; - return NULL; - } - // map CID -> Unicode -> GID - if (ctu->isIdentity()) { - codeToGIDLen = 65536; - } else { - codeToGIDLen = ctu->getLength(); - } - codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); - for (code = 0; code < codeToGIDLen; ++code) { - if (ctu->mapToUnicode(code, uBuf, 8) > 0) { - codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]); - } else { - codeToGID[code] = 0; - } - } - ctu->decRefCnt(); - - // check if font is already embedded - fontFileInfo->startIter(&iter); - while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { - if (ff->loc == psFontFileExternal && - ff->type == font->getType() && - !ff->extFileName->cmp(fileName) && - ff->codeToGIDLen == codeToGIDLen && - ff->codeToGID && - !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) { - fontFileInfo->killIter(&iter); - gfree(codeToGID); - delete ffTT; - return ff; - } - } - - // generate name - psName = makePSFontName(font, font->getID()); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // convert it to a Type 0 font - if (globalParams->getPSLevel() >= psLevel3) { - // Level 3: use a CID font - ffTT->convertToCIDType0(psName->getCString(), - codeToGID, codeToGIDLen, - outputFunc, outputStream); - } else { - // otherwise: use a non-CID composite font - ffTT->convertToType0(psName->getCString(), - codeToGID, codeToGIDLen, - outputFunc, outputStream); - } - delete ffTT; - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); - ff->extFileName = fileName->copy(); - ff->codeToGID = codeToGID; - ff->codeToGIDLen = codeToGIDLen; - fontFileInfo->add(ff->psName, ff); - return ff; -} - -PSFontFileInfo *PSOutputDev::setupType3Font(GfxFont *font, - Dict *parentResDict) { - PSFontFileInfo *ff; - GString *psName; - Dict *resDict; - Dict *charProcs; - Object charProc; - Gfx *gfx; - PDFRectangle box; - double *m; - GString *buf; - int i; - - // generate name - psName = GString::format("T3_{0:d}_{1:d}", - font->getID()->num, font->getID()->gen); - - // set up resources used by font - if ((resDict = ((Gfx8BitFont *)font)->getResources())) { - inType3Char = gTrue; - setupResources(resDict); - inType3Char = gFalse; - } else { - resDict = parentResDict; - } - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName->getCString()); - embFontList->append("\n"); - - // font dictionary - writePS("8 dict begin\n"); - writePS("/FontType 3 def\n"); - m = font->getFontMatrix(); - writePSFmt("/FontMatrix [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", - m[0], m[1], m[2], m[3], m[4], m[5]); - m = font->getFontBBox(); - writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", - m[0], m[1], m[2], m[3]); - writePS("/Encoding 256 array def\n"); - writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); - writePS("/BuildGlyph {\n"); - writePS(" exch /CharProcs get exch\n"); - writePS(" 2 copy known not { pop /.notdef } if\n"); - writePS(" get exec\n"); - writePS("} bind def\n"); - writePS("/BuildChar {\n"); - writePS(" 1 index /Encoding get exch get\n"); - writePS(" 1 index /BuildGlyph get exec\n"); - writePS("} bind def\n"); - if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) { - writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength()); - writePS("CharProcs begin\n"); - box.x1 = m[0]; - box.y1 = m[1]; - box.x2 = m[2]; - box.y2 = m[3]; - gfx = new Gfx(doc, this, resDict, &box, NULL); - inType3Char = gTrue; - for (i = 0; i < charProcs->getLength(); ++i) { - t3FillColorOnly = gFalse; - t3Cacheable = gFalse; - t3NeedsRestore = gFalse; - writePS("/"); - writePSName(charProcs->getKey(i)); - writePS(" {\n"); - gfx->display(charProcs->getValNF(i, &charProc)); - charProc.free(); - if (t3String) { - if (t3Cacheable) { - buf = GString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n", - t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY); - } else { - buf = GString::format("{0:.6g} {1:.6g} setcharwidth\n", t3WX, t3WY); - } - (*outputFunc)(outputStream, buf->getCString(), buf->getLength()); - delete buf; - (*outputFunc)(outputStream, t3String->getCString(), - t3String->getLength()); - delete t3String; - t3String = NULL; - } - if (t3NeedsRestore) { - (*outputFunc)(outputStream, "Q\n", 2); - } - writePS("} def\n"); - } - inType3Char = gFalse; - delete gfx; - writePS("end\n"); - } - writePS("currentdict end\n"); - writePSFmt("/{0:t} exch definefont pop\n", psName); - - // ending comment - writePS("%%EndResource\n"); - - ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); - fontFileInfo->add(ff->psName, ff); - return ff; -} - -// Make a unique PS font name, based on the names given in the PDF -// font object, and an object ID (font file object for -GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) { - GString *psName, *s; - - if ((s = font->getEmbeddedFontName())) { - psName = filterPSName(s); - if (!fontFileInfo->lookup(psName)) { - return psName; - } - delete psName; - } - if ((s = font->getName())) { - psName = filterPSName(s); - if (!fontFileInfo->lookup(psName)) { - return psName; - } - delete psName; - } - psName = GString::format("FF{0:d}_{1:d}", id->num, id->gen); - if ((s = font->getEmbeddedFontName())) { - s = filterPSName(s); - psName->append('_')->append(s); - delete s; - } else if ((s = font->getName())) { - s = filterPSName(s); - psName->append('_')->append(s); - delete s; - } - return psName; -} - -GString *PSOutputDev::fixType1Font(GString *font, int length1, int length2) { - Guchar *fontData; - GString *out, *binSection; - GBool pfb; - int fontSize, i; - - fontData = (Guchar *)font->getCString(); - fontSize = font->getLength(); - - // check for PFB - pfb = fontSize >= 6 && fontData[0] == 0x80 && fontData[1] == 0x01; - out = new GString(); - binSection = new GString(); - if (pfb) { - if (!splitType1PFB(fontData, fontSize, out, binSection)) { - delete out; - delete binSection; - return copyType1PFB(fontData, fontSize); - } - } else { - if (!splitType1PFA(fontData, fontSize, length1, length2, - out, binSection)) { - delete out; - delete binSection; - return copyType1PFA(fontData, fontSize); - } - } - - out->append('\n'); - - binSection = asciiHexDecodeType1EexecSection(binSection); - - if (!fixType1EexecSection(binSection, out)) { - delete out; - delete binSection; - return pfb ? copyType1PFB(fontData, fontSize) - : copyType1PFA(fontData, fontSize); - } - delete binSection; - - for (i = 0; i < 8; ++i) { - out->append("0000000000000000000000000000000000000000000000000000000000000000\n"); - } - out->append("cleartomark\n"); - - return out; -} - -// Split a Type 1 font in PFA format into a text section and a binary -// section. -GBool PSOutputDev::splitType1PFA(Guchar *font, int fontSize, - int length1, int length2, - GString *textSection, GString *binSection) { - int textLength, binStart, binLength, lastSpace, i; - - //--- extract the text section - - // Length1 is correct, and the text section ends with whitespace - if (length1 <= fontSize && - length1 >= 18 && - !memcmp(font + length1 - 18, "currentfile eexec", 17)) { - textLength = length1 - 1; - - // Length1 is correct, but the trailing whitespace is missing - } else if (length1 <= fontSize && - length1 >= 17 && - !memcmp(font + length1 - 17, "currentfile eexec", 17)) { - textLength = length1; - - // Length1 is incorrect - } else { - for (textLength = 17; textLength <= fontSize; ++textLength) { - if (!memcmp(font + textLength - 17, "currentfile eexec", 17)) { - break; - } - } - if (textLength > fontSize) { - return gFalse; - } - } - - textSection->append((char *)font, textLength); - - //--- skip whitespace between the text section and the binary section - - for (i = 0, binStart = textLength; - i < 8 && binStart < fontSize; - ++i, ++binStart) { - if (font[binStart] != ' ' && font[binStart] != '\t' && - font[binStart] != '\n' && font[binStart] != '\r') { - break; - } - } - if (i == 8) { - return gFalse; - } - - //--- extract binary section - - // if we see "0000", assume Length2 is correct - // (if Length2 is too long, it will be corrected by fixType1EexecSection) - if (length2 > 0 && length2 < INT_MAX - 4 && - binStart <= fontSize - length2 - 4 && - !memcmp(font + binStart + length2, "0000", 4)) { - binLength = length2; - - } else { - - // look for "0000" near the end of the font (note that there can - // be intervening "\n", "\r\n", etc.), then search backward - if (fontSize - binStart < 512) { - return gFalse; - } - if (!memcmp(font + fontSize - 256, "0000", 4) || - !memcmp(font + fontSize - 255, "0000", 4) || - !memcmp(font + fontSize - 254, "0000", 4) || - !memcmp(font + fontSize - 253, "0000", 4) || - !memcmp(font + fontSize - 252, "0000", 4) || - !memcmp(font + fontSize - 251, "0000", 4)) { - i = fontSize - 252; - lastSpace = -1; - while (i >= binStart) { - if (font[i] == ' ' || font[i] == '\t' || - font[i] == '\n' || font[i] == '\r') { - lastSpace = i; - --i; - } else if (font[i] == '0') { - --i; - } else { - break; - } - } - if (lastSpace < 0) { - return gFalse; - } - // check for the case where the newline/space is missing between - // the binary section and the first set of 64 '0' chars - if (lastSpace - binStart > 64 && - !memcmp(font + lastSpace - 64, - "0000000000000000000000000000000000000000000000000000000000000000", - 64)) { - binLength = lastSpace - 64 - binStart; - } else { - binLength = lastSpace - binStart; - } - - // couldn't find zeros after binary section -- assume they're - // missing and the binary section extends to the end of the file - } else { - binLength = fontSize - binStart; - } - } - - binSection->append((char *)(font + binStart), binLength); - - return gTrue; -} - -// Split a Type 1 font in PFB format into a text section and a binary -// section. -GBool PSOutputDev::splitType1PFB(Guchar *font, int fontSize, - GString *textSection, GString *binSection) { - Guchar *p; - int state, remain, len, n; - - // states: - // 0: text section - // 1: binary section - // 2: trailer section - // 3: eof - - state = 0; - p = font; - remain = fontSize; - while (remain >= 2) { - if (p[0] != 0x80) { - return gFalse; - } - switch (state) { - case 0: - if (p[1] == 0x02) { - state = 1; - } else if (p[1] != 0x01) { - return gFalse; - } - break; - case 1: - if (p[1] == 0x01) { - state = 2; - } else if (p[1] != 0x02) { - return gFalse; - } - break; - case 2: - if (p[1] == 0x03) { - state = 3; - } else if (p[1] != 0x01) { - return gFalse; - } - break; - default: // shouldn't happen - return gFalse; - } - if (state == 3) { - break; - } - - if (remain < 6) { - break; - } - len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24); - if (len < 0 || len > remain - 6) { - return gFalse; - } - - switch (state) { - case 0: - textSection->append((char *)(p + 6), len); - break; - case 1: - binSection->append((char *)(p + 6), len); - break; - case 2: - // we don't use the trailer - break; - default: // shouldn't happen - return gFalse; - } - - p += len + 6; - remain -= len + 6; - } - - if (state != 3) { - return gFalse; - } - - n = textSection->getLength(); - if (n >= 18 && !memcmp(textSection->getCString() + n - 18, - "currentfile eexec", 17)) { - // remove the trailing whitespace - textSection->del(n - 1, 1); - } else if (n >= 17 && !memcmp(textSection->getCString() + n - 17, - "currentfile eexec", 17)) { - // missing whitespace at end -- leave as-is - } else { - return gFalse; - } - - return gTrue; -} - -// If is ASCIIHex-encoded, decode it, delete , and return the -// binary version. Else return unchanged. -GString *PSOutputDev::asciiHexDecodeType1EexecSection(GString *in) { - GString *out; - char c; - Guchar byte; - int state, i; - - out = new GString(); - state = 0; - byte = 0; - for (i = 0; i < in->getLength(); ++i) { - c = in->getChar(i); - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { - continue; - } - if (c >= '0' && c <= '9') { - byte = (Guchar)(byte + (c - '0')); - } else if (c >= 'A' && c <= 'F') { - byte = (Guchar)(byte + (c - 'A' + 10)); - } else if (c >= 'a' && c <= 'f') { - byte = (Guchar)(byte + (c - 'a' + 10)); - } else { - delete out; - return in; - } - if (state == 0) { - byte = (Guchar)(byte << 4); - state = 1; - } else { - out->append((char)byte); - state = 0; - byte = 0; - } - } - delete in; - return out; -} - -GBool PSOutputDev::fixType1EexecSection(GString *binSection, GString *out) { - static char hexChars[17] = "0123456789abcdef"; - Guchar buf[16], buf2[16]; - Guchar byte; - int r, i, j; - - // eexec-decode the binary section, keeping the last 16 bytes - r = 55665; - for (i = 0; i < binSection->getLength(); ++i) { - byte = (Guchar)binSection->getChar(i); - buf[i & 15] = byte ^ (Guchar)(r >> 8); - r = ((r + byte) * 52845 + 22719) & 0xffff; - } - for (j = 0; j < 16; ++j) { - buf2[j] = buf[(i + j) & 15]; - } - - // look for 'closefile' - for (i = 0; i <= 16 - 9; ++i) { - if (!memcmp(buf2 + i, "closefile", 9)) { - break; - } - } - if (i > 16 - 9) { - return gFalse; - } - // three cases: - // - short: missing space after "closefile" (i == 16 - 9) - // - correct: exactly one space after "closefile" (i == 16 - 10) - // - long: extra chars after "closefile" (i < 16 - 10) - if (i == 16 - 9) { - binSection->append((char)((Guchar)'\n' ^ (Guchar)(r >> 8))); - } else if (i < 16 - 10) { - binSection->del(binSection->getLength() - (16 - 10 - i), 16 - 10 - i); - } - - // ASCIIHex encode - for (i = 0; i < binSection->getLength(); i += 32) { - for (j = 0; j < 32 && i+j < binSection->getLength(); ++j) { - byte = (Guchar)binSection->getChar(i+j); - out->append(hexChars[(byte >> 4) & 0x0f]); - out->append(hexChars[byte & 0x0f]); - } - out->append('\n'); - } - - return gTrue; -} - -// The Type 1 cleanup code failed -- assume it's a valid PFA-format -// font and copy it to the output. -GString *PSOutputDev::copyType1PFA(Guchar *font, int fontSize) { - GString *out; - - error(errSyntaxWarning, -1, "Couldn't parse embedded Type 1 font"); - - out = new GString((char *)font, fontSize); - // append a newline to avoid problems where the original font - // doesn't end with one - out->append('\n'); - return out; -} - -// The Type 1 cleanup code failed -- assume it's a valid PFB-format -// font, decode the PFB blocks, and copy them to the output. -GString *PSOutputDev::copyType1PFB(Guchar *font, int fontSize) { - static char hexChars[17] = "0123456789abcdef"; - GString *out; - Guchar *p; - int remain, len, i, j; - - error(errSyntaxWarning, -1, "Couldn't parse embedded Type 1 (PFB) font"); - - out = new GString(); - p = font; - remain = fontSize; - while (remain >= 6 && - p[0] == 0x80 && - (p[1] == 0x01 || p[1] == 0x02)) { - len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24); - if (len > remain - 6) { - break; - } - if (p[1] == 0x01) { - out->append((char *)(p + 6), len); - } else { - for (i = 0; i < len; i += 32) { - for (j = 0; j < 32 && i+j < len; ++j) { - out->append(hexChars[(p[6+i+j] >> 4) & 0x0f]); - out->append(hexChars[p[6+i+j] & 0x0f]); - } - out->append('\n'); - } - } - p += len + 6; - remain -= len + 6; - } - // append a newline to avoid problems where the original font - // doesn't end with one - out->append('\n'); - return out; -} - -void PSOutputDev::renameType1Font(GString *font, GString *name) { - char *p1, *p2; - int i; - - if (!(p1 = strstr(font->getCString(), "\n/FontName")) && - !(p1 = strstr(font->getCString(), "\r/FontName"))) { - return; - } - p1 += 10; - while (*p1 == ' ' || *p1 == '\t' || *p1 == '\n' || *p1 == '\r') { - ++p1; - } - if (*p1 != '/') { - return; - } - ++p1; - p2 = p1; - while (*p2 && *p2 != ' ' && *p2 != '\t' && *p2 != '\n' && *p2 != '\r') { - ++p2; - } - if (!*p2) { - return; - } - i = (int)(p1 - font->getCString()); - font->del(i, (int)(p2 - p1)); - font->insert(i, name); -} - -void PSOutputDev::setupDefaultFont() { - writePS("/xpdf_default_font /Helvetica 1 1 ISOLatin1Encoding pdfMakeFont\n"); -} - -void PSOutputDev::setupImages(Dict *resDict) { - Object xObjDict, xObj, xObjRef, subtypeObj, maskObj, maskRef; - Ref imgID; - int i, j; - - if (!(mode == psModeForm || inType3Char || preload)) { - return; - } - - resDict->lookup("XObject", &xObjDict); - if (xObjDict.isDict()) { - for (i = 0; i < xObjDict.dictGetLength(); ++i) { - xObjDict.dictGetValNF(i, &xObjRef); - xObjDict.dictGetVal(i, &xObj); - if (xObj.isStream()) { - xObj.streamGetDict()->lookup("Subtype", &subtypeObj); - if (subtypeObj.isName("Image")) { - if (xObjRef.isRef()) { - imgID = xObjRef.getRef(); - for (j = 0; j < imgIDLen; ++j) { - if (imgIDs[j].num == imgID.num && imgIDs[j].gen == imgID.gen) { - break; - } - } - if (j == imgIDLen) { - if (imgIDLen >= imgIDSize) { - if (imgIDSize == 0) { - imgIDSize = 64; - } else { - imgIDSize *= 2; - } - imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); - } - imgIDs[imgIDLen++] = imgID; - setupImage(imgID, xObj.getStream(), gFalse, NULL); - if (level >= psLevel3) { - xObj.streamGetDict()->lookup("Mask", &maskObj); - if (maskObj.isStream()) { - setupImage(imgID, maskObj.getStream(), gTrue, NULL); - } else if (level == psLevel3Gray && maskObj.isArray()) { - setupImage(imgID, xObj.getStream(), gFalse, - maskObj.getArray()); - } - maskObj.free(); - } - } - } else { - error(errSyntaxError, -1, - "Image in resource dict is not an indirect reference"); - } - } - subtypeObj.free(); - } - xObj.free(); - xObjRef.free(); - } - } - xObjDict.free(); -} - -void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask, - Array *colorKeyMask) { - StreamColorSpaceMode csMode; - GfxColorSpace *colorSpace; - GfxImageColorMap *colorMap; - int maskColors[2*gfxColorMaxComps]; - Object obj1; - GBool imageMask, useLZW, useRLE, useCompressed, useASCIIHex; - GString *s; - int c, width, height, bits, size, line, col, i; - - // check for mask - str->getDict()->lookup("ImageMask", &obj1); - if (obj1.isBool()) { - imageMask = obj1.getBool(); - } else { - imageMask = gFalse; - } - obj1.free(); - - // get image size - str->getDict()->lookup("Width", &obj1); - if (!obj1.isInt() || obj1.getInt() <= 0) { - error(errSyntaxError, -1, "Invalid Width in image"); - obj1.free(); - return; - } - width = obj1.getInt(); - obj1.free(); - str->getDict()->lookup("Height", &obj1); - if (!obj1.isInt() || obj1.getInt() <= 0) { - error(errSyntaxError, -1, "Invalid Height in image"); - obj1.free(); - return; - } - height = obj1.getInt(); - obj1.free(); - - // build the color map - if (mask || imageMask) { - colorMap = NULL; - } else { - bits = 0; - csMode = streamCSNone; - str->getImageParams(&bits, &csMode); - if (bits == 0) { - str->getDict()->lookup("BitsPerComponent", &obj1); - if (!obj1.isInt()) { - error(errSyntaxError, -1, "Invalid BitsPerComponent in image"); - obj1.free(); - return; - } - bits = obj1.getInt(); - obj1.free(); - } - str->getDict()->lookup("ColorSpace", &obj1); - if (!obj1.isNull()) { - colorSpace = GfxColorSpace::parse(&obj1 - ); - } else if (csMode == streamCSDeviceGray) { - colorSpace = GfxColorSpace::create(csDeviceGray); - } else if (csMode == streamCSDeviceRGB) { - colorSpace = GfxColorSpace::create(csDeviceRGB); - } else if (csMode == streamCSDeviceCMYK) { - colorSpace = GfxColorSpace::create(csDeviceCMYK); - } else { - colorSpace = NULL; - } - obj1.free(); - if (!colorSpace) { - error(errSyntaxError, -1, "Invalid ColorSpace in image"); - return; - } - str->getDict()->lookup("Decode", &obj1); - colorMap = new GfxImageColorMap(bits, &obj1, colorSpace); - obj1.free(); - } - - // filters - if (level < psLevel2) { - useLZW = useRLE = gFalse; - useCompressed = gFalse; - useASCIIHex = gTrue; - } else { - if (colorKeyMask) { - if (globalParams->getPSUncompressPreloadedImages()) { - useLZW = useRLE = gFalse; - } else if (globalParams->getPSLZW()) { - useLZW = gTrue; - useRLE = gFalse; - } else { - useRLE = gTrue; - useLZW = gFalse; - } - useCompressed = gFalse; - } else if (colorMap && - (colorMap->getColorSpace()->getMode() == csDeviceN || - level == psLevel2Gray || level == psLevel3Gray)) { - if (globalParams->getPSLZW()) { - useLZW = gTrue; - useRLE = gFalse; - } else { - useRLE = gTrue; - useLZW = gFalse; - } - useCompressed = gFalse; - } else if (globalParams->getPSUncompressPreloadedImages()) { - useLZW = useRLE = gFalse; - useCompressed = gFalse; - } else { - s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); - if (s) { - useLZW = useRLE = gFalse; - useCompressed = gTrue; - delete s; - } else { - if (globalParams->getPSLZW()) { - useLZW = gTrue; - useRLE = gFalse; - } else { - useRLE = gTrue; - useLZW = gFalse; - } - useCompressed = gFalse; - } - } - useASCIIHex = globalParams->getPSASCIIHex(); - } - if (useCompressed) { - str = str->getUndecodedStream(); - } - if (colorKeyMask) { - memset(maskColors, 0, sizeof(maskColors)); - for (i = 0; i < colorKeyMask->getLength() && i < 2*gfxColorMaxComps; ++i) { - colorKeyMask->get(i, &obj1); - if (obj1.isInt()) { - maskColors[i] = obj1.getInt(); - } - obj1.free(); - } - str = new ColorKeyToMaskEncoder(str, width, height, colorMap, maskColors); - } else if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) { - str = new GrayRecoder(str, width, height, colorMap); - } else if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { - str = new DeviceNRecoder(str, width, height, colorMap); - } - if (useLZW) { - str = new LZWEncoder(str); - } else if (useRLE) { - str = new RunLengthEncoder(str); - } - if (useASCIIHex) { - str = new ASCIIHexEncoder(str); - } else { - str = new ASCII85Encoder(str); - } - - // compute image data size - str->reset(); - col = size = 0; - do { - do { - c = str->getChar(); - } while (c == '\n' || c == '\r'); - if (c == (useASCIIHex ? '>' : '~') || c == EOF) { - break; - } - if (c == 'z') { - ++col; - } else { - ++col; - for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { - do { - c = str->getChar(); - } while (c == '\n' || c == '\r'); - if (c == (useASCIIHex ? '>' : '~') || c == EOF) { - break; - } - ++col; - } - } - if (col > 225) { - ++size; - col = 0; - } - } while (c != (useASCIIHex ? '>' : '~') && c != EOF); - // add one entry for the final line of data; add another entry - // because the LZWDecode/RunLengthDecode filter may read past the end - ++size; - if (useLZW || useRLE) { - ++size; - } - writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n", - size, (mask || colorKeyMask) ? "Mask" : "Im", id.num, id.gen); - str->close(); - - // write the data into the array - str->reset(); - line = col = 0; - writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~")); - do { - do { - c = str->getChar(); - } while (c == '\n' || c == '\r'); - if (c == (useASCIIHex ? '>' : '~') || c == EOF) { - break; - } - if (c == 'z') { - writePSChar((char)c); - ++col; - } else { - writePSChar((char)c); - ++col; - for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { - do { - c = str->getChar(); - } while (c == '\n' || c == '\r'); - if (c == (useASCIIHex ? '>' : '~') || c == EOF) { - break; - } - writePSChar((char)c); - ++col; - } - } - // each line is: "dup nnnnn <~...data...~> put" - // so max data length = 255 - 20 = 235 - // chunks are 1 or 4 bytes each, so we have to stop at 232 - // but make it 225 just to be safe - if (col > 225) { - writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n")); - ++line; - writePSFmt((char *)(useASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line); - col = 0; - } - } while (c != (useASCIIHex ? '>' : '~') && c != EOF); - writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n")); - if (useLZW || useRLE) { - ++line; - writePSFmt("{0:d} <> put\n", line); - } else { - writePS("pop\n"); - } - str->close(); - - delete str; - - if (colorMap) { - delete colorMap; - } -} - -void PSOutputDev::setupForms(Dict *resDict) { - Object xObjDict, xObj, xObjRef, subtypeObj; - int i; - - if (!preload) { - return; - } - - resDict->lookup("XObject", &xObjDict); - if (xObjDict.isDict()) { - for (i = 0; i < xObjDict.dictGetLength(); ++i) { - xObjDict.dictGetValNF(i, &xObjRef); - xObjDict.dictGetVal(i, &xObj); - if (xObj.isStream()) { - xObj.streamGetDict()->lookup("Subtype", &subtypeObj); - if (subtypeObj.isName("Form")) { - if (xObjRef.isRef()) { - setupForm(&xObjRef, &xObj); - } else { - error(errSyntaxError, -1, - "Form in resource dict is not an indirect reference"); - } - } - subtypeObj.free(); - } - xObj.free(); - xObjRef.free(); - } - } - xObjDict.free(); -} - -void PSOutputDev::setupForm(Object *strRef, Object *strObj) { - Dict *dict, *resDict; - Object matrixObj, bboxObj, resObj, obj1; - double m[6], bbox[4]; - PDFRectangle box; - Gfx *gfx; - int i; - - // check if form is already defined - for (i = 0; i < formIDLen; ++i) { - if (formIDs[i].num == strRef->getRefNum() && - formIDs[i].gen == strRef->getRefGen()) { - return; - } - } - - // add entry to formIDs list - if (formIDLen >= formIDSize) { - if (formIDSize == 0) { - formIDSize = 64; - } else { - formIDSize *= 2; - } - formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref)); - } - formIDs[formIDLen++] = strRef->getRef(); - - dict = strObj->streamGetDict(); - - // get bounding box - dict->lookup("BBox", &bboxObj); - if (!bboxObj.isArray()) { - bboxObj.free(); - error(errSyntaxError, -1, "Bad form bounding box"); - return; - } - for (i = 0; i < 4; ++i) { - bboxObj.arrayGet(i, &obj1); - bbox[i] = obj1.getNum(); - obj1.free(); - } - bboxObj.free(); - - // get matrix - dict->lookup("Matrix", &matrixObj); - if (matrixObj.isArray()) { - for (i = 0; i < 6; ++i) { - matrixObj.arrayGet(i, &obj1); - m[i] = obj1.getNum(); - obj1.free(); - } - } else { - m[0] = 1; m[1] = 0; - m[2] = 0; m[3] = 1; - m[4] = 0; m[5] = 0; - } - matrixObj.free(); - - // get resources - dict->lookup("Resources", &resObj); - resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL; - - writePSFmt("/f_{0:d}_{1:d} {{\n", strRef->getRefNum(), strRef->getRefGen()); - writePS("q\n"); - writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n", - m[0], m[1], m[2], m[3], m[4], m[5]); - - box.x1 = bbox[0]; - box.y1 = bbox[1]; - box.x2 = bbox[2]; - box.y2 = bbox[3]; - gfx = new Gfx(doc, this, resDict, &box, &box); - gfx->display(strRef); - delete gfx; - - writePS("Q\n"); - writePS("} def\n"); - - resObj.free(); -} - -GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI, - int rotateA, GBool useMediaBox, GBool crop, - int sliceX, int sliceY, - int sliceW, int sliceH, - GBool printing, - GBool (*abortCheckCbk)(void *data), - void *abortCheckCbkData) { - int pg; -#if HAVE_SPLASH - GBool mono; - GBool useLZW; - double dpi; - SplashOutputDev *splashOut; - SplashColor paperColor; - PDFRectangle box; - GfxState *state; - SplashBitmap *bitmap; - Stream *str0, *str; - Object obj; - Guchar *p; - Guchar col[4]; - char buf[4096]; - double userUnit, hDPI2, vDPI2; - double m0, m1, m2, m3, m4, m5; - int nStripes, stripeH, stripeY; - int w, h, x, y, comp, i, n; -#endif - - pg = page->getNum(); - if (!(pg >= firstPage && pg <= lastPage && - rasterizePage[pg - firstPage])) { - return gTrue; - } - -#if HAVE_SPLASH - // get the rasterization parameters - dpi = globalParams->getPSRasterResolution(); - mono = globalParams->getPSRasterMono() || - level == psLevel1 || - level == psLevel2Gray || - level == psLevel3Gray; - useLZW = globalParams->getPSLZW(); - - // get the UserUnit - if (honorUserUnit) { - userUnit = page->getUserUnit(); - } else { - userUnit = 1; - } - - // start the PS page - page->makeBox(userUnit * dpi, userUnit * dpi, rotateA, useMediaBox, gFalse, - sliceX, sliceY, sliceW, sliceH, &box, &crop); - rotateA += page->getRotate(); - if (rotateA >= 360) { - rotateA -= 360; - } else if (rotateA < 0) { - rotateA += 360; - } - state = new GfxState(dpi, dpi, &box, rotateA, gFalse); - startPage(page->getNum(), state); - delete state; - - // set up the SplashOutputDev - if (mono) { - paperColor[0] = 0xff; - splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, - paperColor, gFalse, - globalParams->getAntialiasPrinting()); -#if SPLASH_CMYK - } else if (level == psLevel1Sep) { - paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0; - splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse, - paperColor, gFalse, - globalParams->getAntialiasPrinting()); -#endif - } else { - paperColor[0] = paperColor[1] = paperColor[2] = 0xff; - splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, - paperColor, gFalse, - globalParams->getAntialiasPrinting()); - } - splashOut->startDoc(xref); - - // break the page into stripes - // NB: startPage() has already multiplied xScale and yScale by UserUnit - hDPI2 = xScale * dpi; - vDPI2 = yScale * dpi; - if (sliceW < 0 || sliceH < 0) { - if (useMediaBox) { - box = *page->getMediaBox(); - } else { - box = *page->getCropBox(); - } - sliceX = sliceY = 0; - sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0); - sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0); - } - nStripes = (int)ceil(((double)sliceW * (double)sliceH) / - (double)globalParams->getPSRasterSliceSize()); - stripeH = (sliceH + nStripes - 1) / nStripes; - - // render the stripes - for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) { - - // rasterize a stripe - page->makeBox(hDPI2, vDPI2, 0, useMediaBox, gFalse, - sliceX, stripeY, sliceW, stripeH, &box, &crop); - m0 = box.x2 - box.x1; - m1 = 0; - m2 = 0; - m3 = box.y2 - box.y1; - m4 = box.x1; - m5 = box.y1; - page->displaySlice(splashOut, hDPI2, vDPI2, - (360 - page->getRotate()) % 360, useMediaBox, crop, - sliceX, stripeY, sliceW, stripeH, - printing, abortCheckCbk, abortCheckCbkData); - - // draw the rasterized image - bitmap = splashOut->getBitmap(); - w = bitmap->getWidth(); - h = bitmap->getHeight(); - writePS("gsave\n"); - writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", - m0, m1, m2, m3, m4, m5); - switch (level) { - case psLevel1: - writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", - w, h, w, -h, h); - p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); - i = 0; - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - writePSFmt("{0:02x}", *p++); - if (++i == 32) { - writePSChar('\n'); - i = 0; - } - } - } - if (i != 0) { - writePSChar('\n'); - } - break; - case psLevel1Sep: - writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", - w, h, w, -h, h); - p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); - i = 0; - col[0] = col[1] = col[2] = col[3] = 0; - for (y = 0; y < h; ++y) { - for (comp = 0; comp < 4; ++comp) { - for (x = 0; x < w; ++x) { - writePSFmt("{0:02x}", p[4*x + comp]); - col[comp] |= p[4*x + comp]; - if (++i == 32) { - writePSChar('\n'); - i = 0; - } - } - } - p -= bitmap->getRowSize(); - } - if (i != 0) { - writePSChar('\n'); - } - if (col[0]) { - processColors |= psProcessCyan; - } - if (col[1]) { - processColors |= psProcessMagenta; - } - if (col[2]) { - processColors |= psProcessYellow; - } - if (col[3]) { - processColors |= psProcessBlack; - } - break; - case psLevel2: - case psLevel2Gray: - case psLevel2Sep: - case psLevel3: - case psLevel3Gray: - case psLevel3Sep: - if (mono) { - writePS("/DeviceGray setcolorspace\n"); - } else { - writePS("/DeviceRGB setcolorspace\n"); - } - writePS("<<\n /ImageType 1\n"); - writePSFmt(" /Width {0:d}\n", bitmap->getWidth()); - writePSFmt(" /Height {0:d}\n", bitmap->getHeight()); - writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h); - writePS(" /BitsPerComponent 8\n"); - if (mono) { - writePS(" /Decode [0 1]\n"); - } else { - writePS(" /Decode [0 1 0 1 0 1]\n"); - } - writePS(" /DataSource currentfile\n"); - if (globalParams->getPSASCIIHex()) { - writePS(" /ASCIIHexDecode filter\n"); - } else { - writePS(" /ASCII85Decode filter\n"); - } - if (useLZW) { - writePS(" /LZWDecode filter\n"); - } else { - writePS(" /RunLengthDecode filter\n"); - } - writePS(">>\n"); - writePS("image\n"); - obj.initNull(); - p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); - str0 = new MemStream((char *)p, 0, w * h * (mono ? 1 : 3), &obj); - if (useLZW) { - str = new LZWEncoder(str0); - } else { - str = new RunLengthEncoder(str0); - } - if (globalParams->getPSASCIIHex()) { - str = new ASCIIHexEncoder(str); - } else { - str = new ASCII85Encoder(str); - } - str->reset(); - while ((n = str->getBlock(buf, sizeof(buf))) > 0) { - writePSBlock(buf, n); - } - str->close(); - delete str; - delete str0; - writePSChar('\n'); - processColors |= mono ? psProcessBlack : psProcessCMYK; - break; - } - writePS("grestore\n"); - } - - delete splashOut; - - // finish the PS page - endPage(); - - return gFalse; - -#else // HAVE_SPLASH - - error(errSyntaxWarning, -1, - "PDF page uses transparency and PSOutputDev was built without" - " the Splash rasterizer - output may not be correct"); - return gTrue; -#endif // HAVE_SPLASH -} - -void PSOutputDev::startPage(int pageNum, GfxState *state) { - Page *page; - double userUnit; - int x1, y1, x2, y2, width, height, t; - int imgWidth, imgHeight, imgWidth2, imgHeight2; - GBool landscape; - GString *s; - - page = doc->getCatalog()->getPage(pageNum); - if (honorUserUnit) { - userUnit = page->getUserUnit(); - } else { - userUnit = 1; - } - - if (mode == psModePS) { - writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage); - if (paperMatch) { - imgLLX = imgLLY = 0; - if (globalParams->getPSUseCropBoxAsPage()) { - imgURX = (int)ceil(page->getCropWidth() * userUnit); - imgURY = (int)ceil(page->getCropHeight() * userUnit); - } else { - imgURX = (int)ceil(page->getMediaWidth() * userUnit); - imgURY = (int)ceil(page->getMediaHeight() * userUnit); - } - if (state->getRotate() == 90 || state->getRotate() == 270) { - t = imgURX; - imgURX = imgURY; - imgURY = t; - } - writePSFmt("%%PageMedia: {0:d}x{1:d}\n", imgURX, imgURY); - writePSFmt("%%PageBoundingBox: 0 0 {0:d} {1:d}\n", imgURX, imgURY); - } - writePS("%%BeginPageSetup\n"); - } - if (mode != psModeForm) { - writePS("xpdf begin\n"); - } - - // set up paper size for paper=match mode - // NB: this must be done *before* the saveState() for overlays. - if (mode == psModePS && paperMatch) { - writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY); - } - - // underlays - if (underlayCbk) { - (*underlayCbk)(this, underlayCbkData); - } - if (overlayCbk) { - saveState(NULL); - } - - switch (mode) { - - case psModePS: - // rotate, translate, and scale page - imgWidth = imgURX - imgLLX; - imgHeight = imgURY - imgLLY; - x1 = (int)floor(state->getX1()); - y1 = (int)floor(state->getY1()); - x2 = (int)ceil(state->getX2()); - y2 = (int)ceil(state->getY2()); - width = x2 - x1; - height = y2 - y1; - tx = ty = 0; - // rotation and portrait/landscape mode - if (paperMatch) { - rotate = (360 - state->getRotate()) % 360; - landscape = gFalse; - } else if (rotate0 >= 0) { - rotate = (360 - rotate0) % 360; - landscape = gFalse; - } else { - rotate = (360 - state->getRotate()) % 360; - if (rotate == 0 || rotate == 180) { - if ((width < height && imgWidth > imgHeight && height > imgHeight) || - (width > height && imgWidth < imgHeight && width > imgWidth)) { - rotate += 90; - landscape = gTrue; - } else { - landscape = gFalse; - } - } else { // rotate == 90 || rotate == 270 - if ((height < width && imgWidth > imgHeight && width > imgHeight) || - (height > width && imgWidth < imgHeight && height > imgWidth)) { - rotate = 270 - rotate; - landscape = gTrue; - } else { - landscape = gFalse; - } - } - } - writePSFmt("%%PageOrientation: {0:s}\n", - landscape ? "Landscape" : "Portrait"); - writePS("pdfStartPage\n"); - if (rotate == 0) { - imgWidth2 = imgWidth; - imgHeight2 = imgHeight; - } else if (rotate == 90) { - writePS("90 rotate\n"); - ty = -imgWidth; - imgWidth2 = imgHeight; - imgHeight2 = imgWidth; - } else if (rotate == 180) { - writePS("180 rotate\n"); - imgWidth2 = imgWidth; - imgHeight2 = imgHeight; - tx = -imgWidth; - ty = -imgHeight; - } else { // rotate == 270 - writePS("270 rotate\n"); - tx = -imgHeight; - imgWidth2 = imgHeight; - imgHeight2 = imgWidth; - } - // shrink or expand - if (xScale0 > 0 && yScale0 > 0) { - xScale = xScale0 * userUnit; - yScale = yScale0 * userUnit; - } else if ((globalParams->getPSShrinkLarger() && - (width * userUnit > imgWidth2 || - height * userUnit > imgHeight2)) || - (globalParams->getPSExpandSmaller() && - (width * userUnit < imgWidth2 && - height * userUnit < imgHeight2))) { - xScale = (double)imgWidth2 / (double)width; - yScale = (double)imgHeight2 / (double)height; - if (yScale < xScale) { - xScale = yScale; - } else { - yScale = xScale; - } - } else { - xScale = yScale = userUnit; - } - // deal with odd bounding boxes or clipping - if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { - tx -= xScale * clipLLX0; - ty -= yScale * clipLLY0; - } else { - tx -= xScale * x1; - ty -= yScale * y1; - } - // center - if (tx0 >= 0 && ty0 >= 0) { - tx += (rotate == 0 || rotate == 180) ? tx0 : ty0; - ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0; - } else if (globalParams->getPSCenter()) { - if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { - tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2; - ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2; - } else { - tx += (imgWidth2 - xScale * width) / 2; - ty += (imgHeight2 - yScale * height) / 2; - } - } - tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY; - ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX; - if (tx != 0 || ty != 0) { - writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); - } - if (xScale != 1 || yScale != 1) { - writePSFmt("{0:.4f} {1:.4f} scale\n", xScale, yScale); - } - if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { - writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n", - clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0); - } else { - writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1); - } - - ++seqPage; - break; - - case psModeEPS: - writePS("pdfStartPage\n"); - tx = ty = 0; - rotate = (360 - state->getRotate()) % 360; - if (rotate == 0) { - } else if (rotate == 90) { - writePS("90 rotate\n"); - tx = -epsX1; - ty = -epsY2; - } else if (rotate == 180) { - writePS("180 rotate\n"); - tx = -(epsX1 + epsX2); - ty = -(epsY1 + epsY2); - } else { // rotate == 270 - writePS("270 rotate\n"); - tx = -epsX2; - ty = -epsY1; - } - if (tx != 0 || ty != 0) { - writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); - } - xScale = yScale = 1; - break; - - case psModeForm: - writePS("/PaintProc {\n"); - writePS("begin xpdf begin\n"); - writePS("pdfStartPage\n"); - tx = ty = 0; - xScale = yScale = 1; - rotate = 0; - break; - } - - if (level == psLevel2Gray || level == psLevel3Gray) { - writePS("/DeviceGray setcolorspace\n"); - } - - if (customCodeCbk) { - if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum, - customCodeCbkData))) { - writePS(s->getCString()); - delete s; - } - } - - if (mode == psModePS) { - writePS("%%EndPageSetup\n"); - } - - noStateChanges = gFalse; -} - -void PSOutputDev::endPage() { - if (overlayCbk) { - restoreState(NULL); - (*overlayCbk)(this, overlayCbkData); - } - - if (mode == psModeForm) { - writePS("pdfEndPage\n"); - writePS("end end\n"); - writePS("} def\n"); - writePS("end end\n"); - } else { - if (!manualCtrl) { - writePS("showpage\n"); - } - writePS("%%PageTrailer\n"); - writePageTrailer(); - writePS("end\n"); - } -} - -void PSOutputDev::saveState(GfxState *state) { - // The noStateChanges and saveStack fields are used to implement an - // optimization to reduce gsave/grestore nesting. The idea is to - // look for sequences like this: - // q q AAA Q BBB Q (where AAA and BBB are sequences of operations) - // and transform them to: - // q AAA Q q BBB Q - if (noStateChanges) { - // any non-NULL pointer will work here - saveStack->append(this); - } else { - saveStack->append((PSOutputDev *)NULL); - writePS("q\n"); - noStateChanges = gTrue; - } -} - -void PSOutputDev::restoreState(GfxState *state) { - if (saveStack->getLength()) { - writePS("Q\n"); - if (saveStack->del(saveStack->getLength() - 1)) { - writePS("q\n"); - noStateChanges = gTrue; - } else { - noStateChanges = gFalse; - } - } -} - -void PSOutputDev::updateCTM(GfxState *state, double m11, double m12, - double m21, double m22, double m31, double m32) { - if (m11 == 1 && m12 == 0 && m21 == 0 && m22 == 1 && m31 == 0 && m32 == 0) { - return; - } - if (fabs(m11 * m22 - m12 * m21) < 1e-10) { - // avoid a singular (or close-to-singular) matrix - writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] cm\n", m31, m32); - } else { - writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n", - m11, m12, m21, m22, m31, m32); - } - noStateChanges = gFalse; -} - -void PSOutputDev::updateLineDash(GfxState *state) { - double *dash; - double start; - int length, i; - - state->getLineDash(&dash, &length, &start); - writePS("["); - for (i = 0; i < length; ++i) { - writePSFmt("{0:.6g}{1:w}", - dash[i] < 0 ? 0 : dash[i], - (i == length-1) ? 0 : 1); - } - writePSFmt("] {0:.6g} d\n", start); - noStateChanges = gFalse; -} - -void PSOutputDev::updateFlatness(GfxState *state) { - writePSFmt("{0:.4g} i\n", state->getFlatness()); - noStateChanges = gFalse; -} - -void PSOutputDev::updateLineJoin(GfxState *state) { - writePSFmt("{0:d} j\n", state->getLineJoin()); - noStateChanges = gFalse; -} - -void PSOutputDev::updateLineCap(GfxState *state) { - writePSFmt("{0:d} J\n", state->getLineCap()); - noStateChanges = gFalse; -} - -void PSOutputDev::updateMiterLimit(GfxState *state) { - writePSFmt("{0:.4g} M\n", state->getMiterLimit()); - noStateChanges = gFalse; -} - -void PSOutputDev::updateLineWidth(GfxState *state) { - writePSFmt("{0:.6g} w\n", state->getLineWidth()); - noStateChanges = gFalse; -} - -void PSOutputDev::updateFillColorSpace(GfxState *state) { - switch (level) { - case psLevel1: - case psLevel1Sep: - break; - case psLevel2: - case psLevel3: - if (state->getFillColorSpace()->getMode() != csPattern) { - dumpColorSpaceL2(state, state->getFillColorSpace(), - gTrue, gFalse, gFalse); - writePS(" cs\n"); - noStateChanges = gFalse; - } - break; - case psLevel2Gray: - case psLevel3Gray: - case psLevel2Sep: - case psLevel3Sep: - break; - } -} - -void PSOutputDev::updateStrokeColorSpace(GfxState *state) { - switch (level) { - case psLevel1: - case psLevel1Sep: - break; - case psLevel2: - case psLevel3: - if (state->getStrokeColorSpace()->getMode() != csPattern) { - dumpColorSpaceL2(state, state->getStrokeColorSpace(), - gTrue, gFalse, gFalse); - writePS(" CS\n"); - noStateChanges = gFalse; - } - break; - case psLevel2Gray: - case psLevel3Gray: - case psLevel2Sep: - case psLevel3Sep: - break; - } -} - -void PSOutputDev::updateFillColor(GfxState *state) { - GfxColor color; - GfxColor *colorPtr; - GfxGray gray; - GfxCMYK cmyk; - GfxSeparationColorSpace *sepCS; - double c, m, y, k; - int i; - - switch (level) { - case psLevel1: - case psLevel2Gray: - case psLevel3Gray: - state->getFillGray(&gray); - writePSFmt("{0:.4g} g\n", colToDbl(gray)); - break; - case psLevel1Sep: - state->getFillCMYK(&cmyk); - c = colToDbl(cmyk.c); - m = colToDbl(cmyk.m); - y = colToDbl(cmyk.y); - k = colToDbl(cmyk.k); - writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k); - addProcessColor(c, m, y, k); - break; - case psLevel2: - case psLevel3: - if (state->getFillColorSpace()->getMode() != csPattern) { - colorPtr = state->getFillColor(); - writePS("["); - for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) { - if (i > 0) { - writePS(" "); - } - writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i])); - } - writePS("] sc\n"); - } - break; - case psLevel2Sep: - case psLevel3Sep: - if (state->getFillColorSpace()->getMode() == csSeparation) { - sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace(); - color.c[0] = gfxColorComp1; - sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); - writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n", - colToDbl(state->getFillColor()->c[0]), - colToDbl(cmyk.c), colToDbl(cmyk.m), - colToDbl(cmyk.y), colToDbl(cmyk.k), - sepCS->getName()); - addCustomColor(state, sepCS); - } else { - state->getFillCMYK(&cmyk); - c = colToDbl(cmyk.c); - m = colToDbl(cmyk.m); - y = colToDbl(cmyk.y); - k = colToDbl(cmyk.k); - writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k); - addProcessColor(c, m, y, k); - } - break; - } - t3Cacheable = gFalse; - noStateChanges = gFalse; -} - -void PSOutputDev::updateStrokeColor(GfxState *state) { - GfxColor color; - GfxColor *colorPtr; - GfxGray gray; - GfxCMYK cmyk; - GfxSeparationColorSpace *sepCS; - double c, m, y, k; - int i; - - switch (level) { - case psLevel1: - case psLevel2Gray: - case psLevel3Gray: - state->getStrokeGray(&gray); - writePSFmt("{0:.4g} G\n", colToDbl(gray)); - break; - case psLevel1Sep: - state->getStrokeCMYK(&cmyk); - c = colToDbl(cmyk.c); - m = colToDbl(cmyk.m); - y = colToDbl(cmyk.y); - k = colToDbl(cmyk.k); - writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k); - addProcessColor(c, m, y, k); - break; - case psLevel2: - case psLevel3: - if (state->getStrokeColorSpace()->getMode() != csPattern) { - colorPtr = state->getStrokeColor(); - writePS("["); - for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) { - if (i > 0) { - writePS(" "); - } - writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i])); - } - writePS("] SC\n"); - } - break; - case psLevel2Sep: - case psLevel3Sep: - if (state->getStrokeColorSpace()->getMode() == csSeparation) { - sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace(); - color.c[0] = gfxColorComp1; - sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); - writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n", - colToDbl(state->getStrokeColor()->c[0]), - colToDbl(cmyk.c), colToDbl(cmyk.m), - colToDbl(cmyk.y), colToDbl(cmyk.k), - sepCS->getName()); - addCustomColor(state, sepCS); - } else { - state->getStrokeCMYK(&cmyk); - c = colToDbl(cmyk.c); - m = colToDbl(cmyk.m); - y = colToDbl(cmyk.y); - k = colToDbl(cmyk.k); - writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k); - addProcessColor(c, m, y, k); - } - break; - } - t3Cacheable = gFalse; - noStateChanges = gFalse; -} - -void PSOutputDev::addProcessColor(double c, double m, double y, double k) { - if (c > 0) { - processColors |= psProcessCyan; - } - if (m > 0) { - processColors |= psProcessMagenta; - } - if (y > 0) { - processColors |= psProcessYellow; - } - if (k > 0) { - processColors |= psProcessBlack; - } -} - -void PSOutputDev::addCustomColor(GfxState *state, - GfxSeparationColorSpace *sepCS) { - PSOutCustomColor *cc; - GfxColor color; - GfxCMYK cmyk; - - for (cc = customColors; cc; cc = cc->next) { - if (!cc->name->cmp(sepCS->getName())) { - return; - } - } - color.c[0] = gfxColorComp1; - sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); - cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m), - colToDbl(cmyk.y), colToDbl(cmyk.k), - sepCS->getName()->copy()); - cc->next = customColors; - customColors = cc; -} - -void PSOutputDev::addCustomColors(GfxState *state, - GfxDeviceNColorSpace *devnCS) { - PSOutCustomColor *cc; - GfxColor color; - GfxCMYK cmyk; - int i; - - for (i = 0; i < devnCS->getNComps(); ++i) { - color.c[i] = 0; - } - for (i = 0; i < devnCS->getNComps(); ++i) { - for (cc = customColors; cc; cc = cc->next) { - if (!cc->name->cmp(devnCS->getColorantName(i))) { - break; - } - } - if (cc) { - continue; - } - color.c[i] = gfxColorComp1; - devnCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); - color.c[i] = 0; - cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m), - colToDbl(cmyk.y), colToDbl(cmyk.k), - devnCS->getColorantName(i)->copy()); - cc->next = customColors; - customColors = cc; - } -} - -void PSOutputDev::updateFillOverprint(GfxState *state) { - if (level == psLevel2 || level == psLevel2Sep || - level == psLevel3 || level == psLevel3Sep) { - writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false"); - noStateChanges = gFalse; - } -} - -void PSOutputDev::updateStrokeOverprint(GfxState *state) { - if (level == psLevel2 || level == psLevel2Sep || - level == psLevel3 || level == psLevel3Sep) { - writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false"); - noStateChanges = gFalse; - } -} - -void PSOutputDev::updateOverprintMode(GfxState *state) { - if (level == psLevel3 || level == psLevel3Sep) { - writePSFmt("{0:s} opm\n", state->getOverprintMode() ? "true" : "false"); - noStateChanges = gFalse; - } -} - -void PSOutputDev::updateTransfer(GfxState *state) { - Function **funcs; - int i; - - funcs = state->getTransfer(); - if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) { - if (level == psLevel2 || level == psLevel2Sep || - level == psLevel3 || level == psLevel3Sep) { - for (i = 0; i < 4; ++i) { - cvtFunction(funcs[i]); - } - writePS("setcolortransfer\n"); - } else { - cvtFunction(funcs[3]); - writePS("settransfer\n"); - } - } else if (funcs[0]) { - cvtFunction(funcs[0]); - writePS("settransfer\n"); - } else { - writePS("{} settransfer\n"); - } - noStateChanges = gFalse; -} - -void PSOutputDev::updateFont(GfxState *state) { - if (state->getFont()) { - if (state->getFont()->getTag() && - !state->getFont()->getTag()->cmp("xpdf_default_font")) { - writePSFmt("/xpdf_default_font {0:.6g} Tf\n", - fabs(state->getFontSize()) < 0.0001 ? 0.0001 - : state->getFontSize()); - } else { - writePSFmt("/F{0:d}_{1:d} {2:.6g} Tf\n", - state->getFont()->getID()->num, state->getFont()->getID()->gen, - fabs(state->getFontSize()) < 0.0001 ? 0.0001 - : state->getFontSize()); - } - noStateChanges = gFalse; - } -} - -void PSOutputDev::updateTextMat(GfxState *state) { - double *mat; - - mat = state->getTextMat(); - if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 1e-10) { - // avoid a singular (or close-to-singular) matrix - writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat[4], mat[5]); - } else { - writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] Tm\n", - mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - } - noStateChanges = gFalse; -} - -void PSOutputDev::updateCharSpace(GfxState *state) { - writePSFmt("{0:.6g} Tc\n", state->getCharSpace()); - noStateChanges = gFalse; -} - -void PSOutputDev::updateRender(GfxState *state) { - int rm; - - rm = state->getRender(); - writePSFmt("{0:d} Tr\n", rm); - rm &= 3; - if (rm != 0 && rm != 3) { - t3Cacheable = gFalse; - } - noStateChanges = gFalse; -} - -void PSOutputDev::updateRise(GfxState *state) { - writePSFmt("{0:.6g} Ts\n", state->getRise()); - noStateChanges = gFalse; -} - -void PSOutputDev::updateWordSpace(GfxState *state) { - writePSFmt("{0:.6g} Tw\n", state->getWordSpace()); - noStateChanges = gFalse; -} - -void PSOutputDev::updateHorizScaling(GfxState *state) { - double h; - - h = state->getHorizScaling(); - if (fabs(h) < 0.01) { - h = 0.01; - } - writePSFmt("{0:.6g} Tz\n", h); - noStateChanges = gFalse; -} - -void PSOutputDev::updateTextPos(GfxState *state) { - writePSFmt("{0:.6g} {1:.6g} Td\n", state->getLineX(), state->getLineY()); - noStateChanges = gFalse; -} - -void PSOutputDev::updateTextShift(GfxState *state, double shift) { - if (state->getFont()->getWMode()) { - writePSFmt("{0:.6g} TJmV\n", shift); - } else { - writePSFmt("{0:.6g} TJm\n", shift); - } - noStateChanges = gFalse; -} - -void PSOutputDev::saveTextPos(GfxState *state) { - writePS("currentpoint\n"); - noStateChanges = gFalse; -} - -void PSOutputDev::restoreTextPos(GfxState *state) { - writePS("m\n"); - noStateChanges = gFalse; -} - -void PSOutputDev::stroke(GfxState *state) { - doPath(state->getPath()); - if (inType3Char && t3FillColorOnly) { - // if we're constructing a cacheable Type 3 glyph, we need to do - // everything in the fill color - writePS("Sf\n"); - } else { - writePS("S\n"); - } - noStateChanges = gFalse; -} - -void PSOutputDev::fill(GfxState *state) { - doPath(state->getPath()); - writePS("f\n"); - noStateChanges = gFalse; -} - -void PSOutputDev::eoFill(GfxState *state) { - doPath(state->getPath()); - writePS("f*\n"); - noStateChanges = gFalse; -} - -void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef, - int paintType, int tilingType, - Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) { - if (level <= psLevel1Sep) { - tilingPatternFillL1(state, gfx, strRef, paintType, tilingType, - resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep); - } else { - tilingPatternFillL2(state, gfx, strRef, paintType, tilingType, - resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep); - } -} - -void PSOutputDev::tilingPatternFillL1(GfxState *state, Gfx *gfx, - Object *strRef, - int paintType, int tilingType, - Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) { - PDFRectangle box; - Gfx *gfx2; - - // define a Type 3 font - writePS("8 dict begin\n"); - writePS("/FontType 3 def\n"); - writePS("/FontMatrix [1 0 0 1 0 0] def\n"); - writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", - bbox[0], bbox[1], bbox[2], bbox[3]); - writePS("/Encoding 256 array def\n"); - writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); - writePS(" Encoding 120 /x put\n"); - writePS("/BuildGlyph {\n"); - writePS(" exch /CharProcs get exch\n"); - writePS(" 2 copy known not { pop /.notdef } if\n"); - writePS(" get exec\n"); - writePS("} bind def\n"); - writePS("/BuildChar {\n"); - writePS(" 1 index /Encoding get exch get\n"); - writePS(" 1 index /BuildGlyph get exec\n"); - writePS("} bind def\n"); - writePS("/CharProcs 1 dict def\n"); - writePS("CharProcs begin\n"); - box.x1 = bbox[0]; - box.y1 = bbox[1]; - box.x2 = bbox[2]; - box.y2 = bbox[3]; - gfx2 = new Gfx(doc, this, resDict, &box, NULL); - gfx2->takeContentStreamStack(gfx); - writePS("/x {\n"); - if (paintType == 2) { - writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n", - xStep, bbox[0], bbox[1], bbox[2], bbox[3]); - t3FillColorOnly = gTrue; - } else { - if (x1 - 1 <= x0) { - writePS("1 0 setcharwidth\n"); - } else { - writePSFmt("{0:.6g} 0 setcharwidth\n", xStep); - } - t3FillColorOnly = gFalse; - } - inType3Char = gTrue; - ++numTilingPatterns; - gfx2->display(strRef); - --numTilingPatterns; - inType3Char = gFalse; - writePS("} def\n"); - delete gfx2; - writePS("end\n"); - writePS("currentdict end\n"); - writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns); - - // draw the tiles - writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns); - writePS("fCol\n"); - writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", - mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n", - y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1); - writePS("grestore\n"); - noStateChanges = gFalse; -} - -void PSOutputDev::tilingPatternFillL2(GfxState *state, Gfx *gfx, - Object *strRef, - int paintType, int tilingType, - Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) { - PDFRectangle box; - Gfx *gfx2; - - // switch to pattern space - writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", - mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - - // define a pattern - writePSFmt("/xpdfTile{0:d}\n", numTilingPatterns); - writePS("<<\n"); - writePS(" /PatternType 1\n"); - writePSFmt(" /PaintType {0:d}\n", paintType); - writePSFmt(" /TilingType {0:d}\n", tilingType); - writePSFmt(" /BBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}]\n", - bbox[0], bbox[1], bbox[2], bbox[3]); - writePSFmt(" /XStep {0:.6g}\n", xStep); - writePSFmt(" /YStep {0:.6g}\n", yStep); - writePS(" /PaintProc {\n"); - writePS(" pop\n"); - box.x1 = bbox[0]; - box.y1 = bbox[1]; - box.x2 = bbox[2]; - box.y2 = bbox[3]; - gfx2 = new Gfx(doc, this, resDict, &box, NULL); - gfx2->takeContentStreamStack(gfx); - t3FillColorOnly = paintType == 2; - inType3Char = gTrue; - ++numTilingPatterns; - gfx2->display(strRef); - --numTilingPatterns; - inType3Char = gFalse; - delete gfx2; - writePS(" }\n"); - writePS(">> matrix makepattern def\n"); - - // set the pattern - if (paintType == 2) { - writePS("currentcolor "); - } - writePSFmt("xpdfTile{0:d} setpattern\n", numTilingPatterns); - - // fill with the pattern - writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} rectfill\n", - x0 * xStep + bbox[0], - y0 * yStep + bbox[1], - (x1 - x0) * xStep + bbox[2], - (y1 - y0) * yStep + bbox[3]); - - writePS("grestore\n"); - noStateChanges = gFalse; -} - -GBool PSOutputDev::functionShadedFill(GfxState *state, - GfxFunctionShading *shading) { - double x0, y0, x1, y1; - double *mat; - int i; - - if (level == psLevel2Sep || level == psLevel3Sep) { - if (shading->getColorSpace()->getMode() != csDeviceCMYK) { - return gFalse; - } - processColors |= psProcessCMYK; - } - - shading->getDomain(&x0, &y0, &x1, &y1); - mat = shading->getMatrix(); - writePSFmt("/mat [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", - mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); - if (shading->getNFuncs() == 1) { - writePS("/func "); - cvtFunction(shading->getFunc(0)); - writePS("def\n"); - } else { - writePS("/func {\n"); - for (i = 0; i < shading->getNFuncs(); ++i) { - if (i < shading->getNFuncs() - 1) { - writePS("2 copy\n"); - } - cvtFunction(shading->getFunc(i)); - writePS("exec\n"); - if (i < shading->getNFuncs() - 1) { - writePS("3 1 roll\n"); - } - } - writePS("} def\n"); - } - writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0, y0, x1, y1); - - noStateChanges = gFalse; - return gTrue; -} - -GBool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) { - double xMin, yMin, xMax, yMax; - double x0, y0, x1, y1, dx, dy, mul; - double tMin, tMax, t, t0, t1; - int i; - - if (level == psLevel2Sep || level == psLevel3Sep) { - if (shading->getColorSpace()->getMode() != csDeviceCMYK) { - return gFalse; - } - processColors |= psProcessCMYK; - } - - // get the clip region bbox - state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); - - // compute min and max t values, based on the four corners of the - // clip region bbox - shading->getCoords(&x0, &y0, &x1, &y1); - dx = x1 - x0; - dy = y1 - y0; - if (fabs(dx) < 0.01 && fabs(dy) < 0.01) { - return gTrue; - } else { - mul = 1 / (dx * dx + dy * dy); - tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul; - t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul; - if (t < tMin) { - tMin = t; - } else if (t > tMax) { - tMax = t; - } - t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul; - if (t < tMin) { - tMin = t; - } else if (t > tMax) { - tMax = t; - } - t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul; - if (t < tMin) { - tMin = t; - } else if (t > tMax) { - tMax = t; - } - if (tMin < 0 && !shading->getExtend0()) { - tMin = 0; - } - if (tMax > 1 && !shading->getExtend1()) { - tMax = 1; - } - } - - // get the function domain - t0 = shading->getDomain0(); - t1 = shading->getDomain1(); - - // generate the PS code - writePSFmt("/t0 {0:.6g} def\n", t0); - writePSFmt("/t1 {0:.6g} def\n", t1); - writePSFmt("/dt {0:.6g} def\n", t1 - t0); - writePSFmt("/x0 {0:.6g} def\n", x0); - writePSFmt("/y0 {0:.6g} def\n", y0); - writePSFmt("/dx {0:.6g} def\n", x1 - x0); - writePSFmt("/x1 {0:.6g} def\n", x1); - writePSFmt("/y1 {0:.6g} def\n", y1); - writePSFmt("/dy {0:.6g} def\n", y1 - y0); - writePSFmt("/xMin {0:.6g} def\n", xMin); - writePSFmt("/yMin {0:.6g} def\n", yMin); - writePSFmt("/xMax {0:.6g} def\n", xMax); - writePSFmt("/yMax {0:.6g} def\n", yMax); - writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); - if (shading->getNFuncs() == 1) { - writePS("/func "); - cvtFunction(shading->getFunc(0)); - writePS("def\n"); - } else { - writePS("/func {\n"); - for (i = 0; i < shading->getNFuncs(); ++i) { - if (i < shading->getNFuncs() - 1) { - writePS("dup\n"); - } - cvtFunction(shading->getFunc(i)); - writePS("exec\n"); - if (i < shading->getNFuncs() - 1) { - writePS("exch\n"); - } - } - writePS("} def\n"); - } - writePSFmt("{0:.6g} {1:.6g} 0 axialSH\n", tMin, tMax); - - noStateChanges = gFalse; - return gTrue; -} - -GBool PSOutputDev::radialShadedFill(GfxState *state, - GfxRadialShading *shading) { - double xMin, yMin, xMax, yMax; - double x0, y0, r0, x1, y1, r1, t0, t1; - double xa, ya, ra; - double sMin, sMax, h, ta; - double sLeft, sRight, sTop, sBottom, sZero, sDiag; - GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; - GBool haveSMin, haveSMax; - double theta, alpha, a1, a2; - GBool enclosed; - int i; - - if (level == psLevel2Sep || level == psLevel3Sep) { - if (shading->getColorSpace()->getMode() != csDeviceCMYK) { - return gFalse; - } - processColors |= psProcessCMYK; - } - - // get the shading info - shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); - t0 = shading->getDomain0(); - t1 = shading->getDomain1(); - - // Compute the point at which r(s) = 0; check for the enclosed - // circles case; and compute the angles for the tangent lines. - h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); - if (h == 0) { - enclosed = gTrue; - theta = 0; // make gcc happy - } else if (r1 - r0 == 0) { - enclosed = gFalse; - theta = 0; - } else if (fabs(r1 - r0) >= h) { - enclosed = gTrue; - theta = 0; // make gcc happy - } else { - enclosed = gFalse; - theta = asin((r1 - r0) / h); - } - if (enclosed) { - a1 = 0; - a2 = 360; - } else { - alpha = atan2(y1 - y0, x1 - x0); - a1 = (180 / M_PI) * (alpha + theta) + 90; - a2 = (180 / M_PI) * (alpha - theta) - 90; - while (a2 < a1) { - a2 += 360; - } - } - - // compute the (possibly extended) s range - state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); - if (enclosed) { - sMin = 0; - sMax = 1; - } else { - // solve x(sLeft) + r(sLeft) = xMin - if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { - sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); - } else { - sLeft = 0; // make gcc happy - } - // solve x(sRight) - r(sRight) = xMax - if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { - sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); - } else { - sRight = 0; // make gcc happy - } - // solve y(sBottom) + r(sBottom) = yMin - if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { - sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); - } else { - sBottom = 0; // make gcc happy - } - // solve y(sTop) - r(sTop) = yMax - if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { - sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); - } else { - sTop = 0; // make gcc happy - } - // solve r(sZero) = 0 - if ((haveSZero = fabs(r1 - r0) > 0.000001)) { - sZero = -r0 / (r1 - r0); - } else { - sZero = 0; // make gcc happy - } - // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) - if (haveSZero) { - sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + - (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); - } else { - sDiag = 0; // make gcc happy - } - // compute sMin - if (shading->getExtend0()) { - sMin = 0; - haveSMin = gFalse; - if (x0 < x1 && haveSLeft && sLeft < 0) { - sMin = sLeft; - haveSMin = gTrue; - } else if (x0 > x1 && haveSRight && sRight < 0) { - sMin = sRight; - haveSMin = gTrue; - } - if (y0 < y1 && haveSBottom && sBottom < 0) { - if (!haveSMin || sBottom > sMin) { - sMin = sBottom; - haveSMin = gTrue; - } - } else if (y0 > y1 && haveSTop && sTop < 0) { - if (!haveSMin || sTop > sMin) { - sMin = sTop; - haveSMin = gTrue; - } - } - if (haveSZero && sZero < 0) { - if (!haveSMin || sZero > sMin) { - sMin = sZero; - } - } - } else { - sMin = 0; - } - // compute sMax - if (shading->getExtend1()) { - sMax = 1; - haveSMax = gFalse; - if (x1 < x0 && haveSLeft && sLeft > 1) { - sMax = sLeft; - haveSMax = gTrue; - } else if (x1 > x0 && haveSRight && sRight > 1) { - sMax = sRight; - haveSMax = gTrue; - } - if (y1 < y0 && haveSBottom && sBottom > 1) { - if (!haveSMax || sBottom < sMax) { - sMax = sBottom; - haveSMax = gTrue; - } - } else if (y1 > y0 && haveSTop && sTop > 1) { - if (!haveSMax || sTop < sMax) { - sMax = sTop; - haveSMax = gTrue; - } - } - if (haveSZero && sDiag > 1) { - if (!haveSMax || sDiag < sMax) { - sMax = sDiag; - } - } - } else { - sMax = 1; - } - } - - // generate the PS code - writePSFmt("/x0 {0:.6g} def\n", x0); - writePSFmt("/x1 {0:.6g} def\n", x1); - writePSFmt("/dx {0:.6g} def\n", x1 - x0); - writePSFmt("/y0 {0:.6g} def\n", y0); - writePSFmt("/y1 {0:.6g} def\n", y1); - writePSFmt("/dy {0:.6g} def\n", y1 - y0); - writePSFmt("/r0 {0:.6g} def\n", r0); - writePSFmt("/r1 {0:.6g} def\n", r1); - writePSFmt("/dr {0:.6g} def\n", r1 - r0); - writePSFmt("/t0 {0:.6g} def\n", t0); - writePSFmt("/t1 {0:.6g} def\n", t1); - writePSFmt("/dt {0:.6g} def\n", t1 - t0); - writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); - writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false"); - writePSFmt("/a1 {0:.6g} def\n", a1); - writePSFmt("/a2 {0:.6g} def\n", a2); - if (shading->getNFuncs() == 1) { - writePS("/func "); - cvtFunction(shading->getFunc(0)); - writePS("def\n"); - } else { - writePS("/func {\n"); - for (i = 0; i < shading->getNFuncs(); ++i) { - if (i < shading->getNFuncs() - 1) { - writePS("dup\n"); - } - cvtFunction(shading->getFunc(i)); - writePS("exec\n"); - if (i < shading->getNFuncs() - 1) { - writePS("exch\n"); - } - } - writePS("} def\n"); - } - writePSFmt("{0:.6g} {1:.6g} 0 radialSH\n", sMin, sMax); - - // extend the 'enclosed' case - if (enclosed) { - // extend the smaller circle - if ((shading->getExtend0() && r0 <= r1) || - (shading->getExtend1() && r1 < r0)) { - if (r0 <= r1) { - ta = t0; - ra = r0; - xa = x0; - ya = y0; - } else { - ta = t1; - ra = r1; - xa = x1; - ya = y1; - } - if (level == psLevel2Sep || level == psLevel3Sep) { - writePSFmt("{0:.6g} radialCol aload pop k\n", ta); - } else { - writePSFmt("{0:.6g} radialCol sc\n", ta); - } - writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h f*\n", xa, ya, ra); - } - - // extend the larger circle - if ((shading->getExtend0() && r0 > r1) || - (shading->getExtend1() && r1 >= r0)) { - if (r0 > r1) { - ta = t0; - ra = r0; - xa = x0; - ya = y0; - } else { - ta = t1; - ra = r1; - xa = x1; - ya = y1; - } - if (level == psLevel2Sep || level == psLevel3Sep) { - writePSFmt("{0:.6g} radialCol aload pop k\n", ta); - } else { - writePSFmt("{0:.6g} radialCol sc\n", ta); - } - writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h\n", xa, ya, ra); - writePSFmt("{0:.6g} {1:.6g} m {2:.6g} {3:.6g} l {4:.6g} {5:.6g} l {6:.6g} {7:.6g} l h f*\n", - xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin); - } - } - - noStateChanges = gFalse; - return gTrue; -} - -void PSOutputDev::clip(GfxState *state) { - doPath(state->getPath()); - writePS("W\n"); - noStateChanges = gFalse; -} - -void PSOutputDev::eoClip(GfxState *state) { - doPath(state->getPath()); - writePS("W*\n"); - noStateChanges = gFalse; -} - -void PSOutputDev::clipToStrokePath(GfxState *state) { - doPath(state->getPath()); - writePS("Ws\n"); - noStateChanges = gFalse; -} - -void PSOutputDev::doPath(GfxPath *path) { - GfxSubpath *subpath; - double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4; - int n, m, i, j; - - n = path->getNumSubpaths(); - - if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) { - subpath = path->getSubpath(0); - x0 = subpath->getX(0); - y0 = subpath->getY(0); - x4 = subpath->getX(4); - y4 = subpath->getY(4); - if (x4 == x0 && y4 == y0) { - x1 = subpath->getX(1); - y1 = subpath->getY(1); - x2 = subpath->getX(2); - y2 = subpath->getY(2); - x3 = subpath->getX(3); - y3 = subpath->getY(3); - if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) { - writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", - x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1, - fabs(x2 - x0), fabs(y1 - y0)); - return; - } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) { - writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", - x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, - fabs(x1 - x0), fabs(y2 - y0)); - return; - } - } - } - - for (i = 0; i < n; ++i) { - subpath = path->getSubpath(i); - m = subpath->getNumPoints(); - writePSFmt("{0:.6g} {1:.6g} m\n", subpath->getX(0), subpath->getY(0)); - j = 1; - while (j < m) { - if (subpath->getCurve(j)) { - writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} c\n", - subpath->getX(j), subpath->getY(j), - subpath->getX(j+1), subpath->getY(j+1), - subpath->getX(j+2), subpath->getY(j+2)); - j += 3; - } else { - writePSFmt("{0:.6g} {1:.6g} l\n", subpath->getX(j), subpath->getY(j)); - ++j; - } - } - if (subpath->isClosed()) { - writePS("h\n"); - } - } -} - -void PSOutputDev::drawString(GfxState *state, GString *s) { - GfxFont *font; - int wMode; - int *codeToGID; - GString *s2; - double dx, dy, originX, originY, originX0, originY0, tOriginX0, tOriginY0; - char *p; - PSFontInfo *fi; - UnicodeMap *uMap; - CharCode code; - Unicode u[8]; - char buf[8]; - double *dxdy; - int dxdySize, len, nChars, uLen, n, m, i, j; - - // check for invisible text -- this is used by Acrobat Capture - if (state->getRender() == 3) { - return; - } - - // ignore empty strings - if (s->getLength() == 0) { - return; - } - - // get the font - if (!(font = state->getFont())) { - return; - } - wMode = font->getWMode(); - - fi = NULL; - for (i = 0; i < fontInfo->getLength(); ++i) { - fi = (PSFontInfo *)fontInfo->get(i); - if (fi->fontID.num == font->getID()->num && - fi->fontID.gen == font->getID()->gen) { - break; - } - fi = NULL; - } - - // check for a subtitute 16-bit font - uMap = NULL; - codeToGID = NULL; - if (font->isCIDFont()) { - if (!(fi && fi->ff)) { - // font substitution failed, so don't output any text - return; - } - if (fi->ff->encoding) { - uMap = globalParams->getUnicodeMap(fi->ff->encoding); - } - - // check for an 8-bit code-to-GID map - } else { - if (fi && fi->ff) { - codeToGID = fi->ff->codeToGID; - } - } - - // compute the positioning (dx, dy) for each char in the string - nChars = 0; - p = s->getCString(); - len = s->getLength(); - s2 = new GString(); - dxdySize = font->isCIDFont() ? 8 : s->getLength(); - dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double)); - originX0 = originY0 = 0; // make gcc happy - while (len > 0) { - n = font->getNextChar(p, len, &code, - u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, - &dx, &dy, &originX, &originY); - //~ this doesn't handle the case where the origin offset changes - //~ within a string of characters -- which could be fixed by - //~ modifying dx,dy as needed for each character - if (p == s->getCString()) { - originX0 = originX; - originY0 = originY; - } - dx *= state->getFontSize(); - dy *= state->getFontSize(); - if (wMode) { - dy += state->getCharSpace(); - if (n == 1 && *p == ' ') { - dy += state->getWordSpace(); - } - } else { - dx += state->getCharSpace(); - if (n == 1 && *p == ' ') { - dx += state->getWordSpace(); - } - } - dx *= state->getHorizScaling(); - if (font->isCIDFont()) { - if (uMap) { - if (nChars + uLen > dxdySize) { - do { - dxdySize *= 2; - } while (nChars + uLen > dxdySize); - dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); - } - for (i = 0; i < uLen; ++i) { - m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf)); - for (j = 0; j < m; ++j) { - s2->append(buf[j]); - } - //~ this really needs to get the number of chars in the target - //~ encoding - which may be more than the number of Unicode - //~ chars - dxdy[2 * nChars] = dx; - dxdy[2 * nChars + 1] = dy; - ++nChars; - } - } else { - if (nChars + 1 > dxdySize) { - dxdySize *= 2; - dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); - } - s2->append((char)((code >> 8) & 0xff)); - s2->append((char)(code & 0xff)); - dxdy[2 * nChars] = dx; - dxdy[2 * nChars + 1] = dy; - ++nChars; - } - } else { - if (!codeToGID || codeToGID[code] >= 0) { - s2->append((char)code); - dxdy[2 * nChars] = dx; - dxdy[2 * nChars + 1] = dy; - ++nChars; - } - } - p += n; - len -= n; - } - if (uMap) { - uMap->decRefCnt(); - } - originX0 *= state->getFontSize(); - originY0 *= state->getFontSize(); - state->textTransformDelta(originX0, originY0, &tOriginX0, &tOriginY0); - - if (nChars > 0) { - if (wMode) { - writePSFmt("{0:.6g} {1:.6g} rmoveto\n", -tOriginX0, -tOriginY0); - } - writePSString(s2); - writePS("\n["); - for (i = 0; i < 2 * nChars; ++i) { - if (i > 0) { - writePS("\n"); - } - writePSFmt("{0:.6g}", dxdy[i]); - } - if (font->getType() == fontType3) { - writePS("] Tj3\n"); - } else { - writePS("] Tj\n"); - } - if (wMode) { - writePSFmt("{0:.6g} {1:.6g} rmoveto\n", tOriginX0, tOriginY0); - } - } - gfree(dxdy); - delete s2; - - if ((state->getRender() & 4) && font->getType() != fontType3) { - haveTextClip = gTrue; - } - - noStateChanges = gFalse; -} - -void PSOutputDev::endTextObject(GfxState *state) { - if (haveTextClip) { - writePS("Tclip\n"); - haveTextClip = gFalse; - noStateChanges = gFalse; - } -} - -void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool inlineImg, GBool interpolate) { - int len; - - len = height * ((width + 7) / 8); - switch (level) { - case psLevel1: - case psLevel1Sep: - doImageL1(ref, state, NULL, invert, inlineImg, str, width, height, len); - break; - case psLevel2: - case psLevel2Gray: - case psLevel2Sep: - doImageL2(ref, state, NULL, invert, inlineImg, str, width, height, len, - NULL, NULL, 0, 0, gFalse); - break; - case psLevel3: - case psLevel3Gray: - case psLevel3Sep: - doImageL3(ref, state, NULL, invert, inlineImg, str, width, height, len, - NULL, NULL, 0, 0, gFalse); - break; - } - noStateChanges = gFalse; -} - -void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - int *maskColors, GBool inlineImg, - GBool interpolate) { - int len; - - len = height * ((width * colorMap->getNumPixelComps() * - colorMap->getBits() + 7) / 8); - switch (level) { - case psLevel1: - doImageL1(ref, state, colorMap, gFalse, inlineImg, str, - width, height, len); - break; - case psLevel1Sep: - //~ handle indexed, separation, ... color spaces - doImageL1Sep(state, colorMap, gFalse, inlineImg, str, width, height, len); - break; - case psLevel2: - case psLevel2Gray: - case psLevel2Sep: - doImageL2(ref, state, colorMap, gFalse, inlineImg, str, - width, height, len, maskColors, NULL, 0, 0, gFalse); - break; - case psLevel3: - case psLevel3Gray: - case psLevel3Sep: - doImageL3(ref, state, colorMap, gFalse, inlineImg, str, - width, height, len, maskColors, NULL, 0, 0, gFalse); - break; - } - t3Cacheable = gFalse; - noStateChanges = gFalse; -} - -void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - Stream *maskStr, - int maskWidth, int maskHeight, - GBool maskInvert, GBool interpolate) { - int len; - - len = height * ((width * colorMap->getNumPixelComps() * - colorMap->getBits() + 7) / 8); - switch (level) { - case psLevel1: - doImageL1(ref, state, colorMap, gFalse, gFalse, str, width, height, len); - break; - case psLevel1Sep: - //~ handle indexed, separation, ... color spaces - doImageL1Sep(state, colorMap, gFalse, gFalse, str, width, height, len); - break; - case psLevel2: - case psLevel2Gray: - case psLevel2Sep: - doImageL2(ref, state, colorMap, gFalse, gFalse, str, width, height, len, - NULL, maskStr, maskWidth, maskHeight, maskInvert); - break; - case psLevel3: - case psLevel3Gray: - case psLevel3Sep: - doImageL3(ref, state, colorMap, gFalse, gFalse, str, width, height, len, - NULL, maskStr, maskWidth, maskHeight, maskInvert); - break; - } - t3Cacheable = gFalse; - noStateChanges = gFalse; -} - -void PSOutputDev::doImageL1(Object *ref, GfxState *state, - GfxImageColorMap *colorMap, - GBool invert, GBool inlineImg, - Stream *str, int width, int height, int len) { - ImageStream *imgStr; - Guchar pixBuf[gfxColorMaxComps]; - GfxGray gray; - int col, x, y, c, i; - - if ((inType3Char || preload) && !colorMap) { - if (inlineImg) { - // create an array - str = new FixedLengthEncoder(str, len); - str = new ASCIIHexEncoder(str); - str->reset(); - col = 0; - writePS("[<"); - do { - do { - c = str->getChar(); - } while (c == '\n' || c == '\r'); - if (c == '>' || c == EOF) { - break; - } - writePSChar((char)c); - ++col; - // each line is: "<...data...>" - // so max data length = 255 - 4 = 251 - // but make it 240 just to be safe - // chunks are 2 bytes each, so we need to stop on an even col number - if (col == 240) { - writePS(">\n<"); - col = 0; - } - } while (c != '>' && c != EOF); - writePS(">]\n"); - writePS("0\n"); - str->close(); - delete str; - } else { - // set up to use the array already created by setupImages() - writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen()); - } - } - - // image/imagemask command - if ((inType3Char || preload) && !colorMap) { - writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n", - width, height, invert ? "true" : "false", - width, -height, height); - } else if (colorMap) { - writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", - width, height, - width, -height, height); - } else { - writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1\n", - width, height, invert ? "true" : "false", - width, -height, height); - } - - // image data - if (!((inType3Char || preload) && !colorMap)) { - - if (colorMap) { - - // set up to process the data stream - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); - - // process the data stream - i = 0; - for (y = 0; y < height; ++y) { - - // write the line - for (x = 0; x < width; ++x) { - imgStr->getPixel(pixBuf); - colorMap->getGray(pixBuf, &gray, state->getRenderingIntent()); - writePSFmt("{0:02x}", colToByte(gray)); - if (++i == 32) { - writePSChar('\n'); - i = 0; - } - } - } - if (i != 0) { - writePSChar('\n'); - } - str->close(); - delete imgStr; - - // imagemask - } else { - str->reset(); - i = 0; - for (y = 0; y < height; ++y) { - for (x = 0; x < width; x += 8) { - writePSFmt("{0:02x}", str->getChar() & 0xff); - if (++i == 32) { - writePSChar('\n'); - i = 0; - } - } - } - if (i != 0) { - writePSChar('\n'); - } - str->close(); - } - } -} - -void PSOutputDev::doImageL1Sep(GfxState *state, GfxImageColorMap *colorMap, - GBool invert, GBool inlineImg, - Stream *str, int width, int height, int len) { - ImageStream *imgStr; - Guchar *lineBuf; - Guchar pixBuf[gfxColorMaxComps]; - GfxCMYK cmyk; - int x, y, i, comp; - - // width, height, matrix, bits per component - writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", - width, height, - width, -height, height); - - // allocate a line buffer - lineBuf = (Guchar *)gmallocn(width, 4); - - // set up to process the data stream - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); - - // process the data stream - i = 0; - for (y = 0; y < height; ++y) { - - // read the line - for (x = 0; x < width; ++x) { - imgStr->getPixel(pixBuf); - colorMap->getCMYK(pixBuf, &cmyk, state->getRenderingIntent()); - lineBuf[4*x+0] = colToByte(cmyk.c); - lineBuf[4*x+1] = colToByte(cmyk.m); - lineBuf[4*x+2] = colToByte(cmyk.y); - lineBuf[4*x+3] = colToByte(cmyk.k); - addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), - colToDbl(cmyk.y), colToDbl(cmyk.k)); - } - - // write one line of each color component - for (comp = 0; comp < 4; ++comp) { - for (x = 0; x < width; ++x) { - writePSFmt("{0:02x}", lineBuf[4*x + comp]); - if (++i == 32) { - writePSChar('\n'); - i = 0; - } - } - } - } - - if (i != 0) { - writePSChar('\n'); - } - - str->close(); - delete imgStr; - gfree(lineBuf); -} - -void PSOutputDev::doImageL2(Object *ref, GfxState *state, - GfxImageColorMap *colorMap, - GBool invert, GBool inlineImg, - Stream *str, int width, int height, int len, - int *maskColors, Stream *maskStr, - int maskWidth, int maskHeight, GBool maskInvert) { - Stream *str2; - GString *s; - int n, numComps; - GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed; - GfxSeparationColorSpace *sepCS; - GfxColor color; - GfxCMYK cmyk; - char buf[4096]; - int c, col, i; - - // color key masking - if (maskColors && colorMap && !inlineImg) { - // can't read the stream twice for inline images -- but masking - // isn't allowed with inline images anyway - convertColorKeyMaskToClipRects(colorMap, str, width, height, maskColors); - - // explicit masking - } else if (maskStr) { - convertExplicitMaskToClipRects(maskStr, maskWidth, maskHeight, maskInvert); - } - - // color space - if (colorMap && !(level == psLevel2Gray || level == psLevel3Gray)) { - dumpColorSpaceL2(state, colorMap->getColorSpace(), gFalse, gTrue, gFalse); - writePS(" setcolorspace\n"); - } - - useASCIIHex = globalParams->getPSASCIIHex(); - - // set up the image data - if (mode == psModeForm || inType3Char || preload) { - if (inlineImg) { - // create an array - str2 = new FixedLengthEncoder(str, len); - if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) { - str2 = new GrayRecoder(str2, width, height, colorMap); - } - if (globalParams->getPSLZW()) { - str2 = new LZWEncoder(str2); - } else { - str2 = new RunLengthEncoder(str2); - } - if (useASCIIHex) { - str2 = new ASCIIHexEncoder(str2); - } else { - str2 = new ASCII85Encoder(str2); - } - str2->reset(); - col = 0; - writePS((char *)(useASCIIHex ? "[<" : "[<~")); - do { - do { - c = str2->getChar(); - } while (c == '\n' || c == '\r'); - if (c == (useASCIIHex ? '>' : '~') || c == EOF) { - break; - } - if (c == 'z') { - writePSChar((char)c); - ++col; - } else { - writePSChar((char)c); - ++col; - for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { - do { - c = str2->getChar(); - } while (c == '\n' || c == '\r'); - if (c == (useASCIIHex ? '>' : '~') || c == EOF) { - break; - } - writePSChar((char)c); - ++col; - } - } - // each line is: "<~...data...~>" - // so max data length = 255 - 6 = 249 - // chunks are 1 or 5 bytes each, so we have to stop at 245 - // but make it 240 just to be safe - if (col > 240) { - writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); - col = 0; - } - } while (c != (useASCIIHex ? '>' : '~') && c != EOF); - writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); - // add an extra entry because the LZWDecode/RunLengthDecode - // filter may read past the end - writePS("<>]\n"); - writePS("0\n"); - str2->close(); - delete str2; - } else { - // set up to use the array already created by setupImages() - writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen()); - } - } - - // image dictionary - writePS("<<\n /ImageType 1\n"); - - // width, height, matrix, bits per component - writePSFmt(" /Width {0:d}\n", width); - writePSFmt(" /Height {0:d}\n", height); - writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", - width, -height, height); - if (colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN || - level == psLevel2Gray || level == psLevel3Gray)) { - writePS(" /BitsPerComponent 8\n"); - } else { - writePSFmt(" /BitsPerComponent {0:d}\n", - colorMap ? colorMap->getBits() : 1); - } - - // decode - if (colorMap) { - writePS(" /Decode ["); - if ((level == psLevel2Sep || level == psLevel3Sep) && - colorMap->getColorSpace()->getMode() == csSeparation) { - // this matches up with the code in the pdfImSep operator - n = (1 << colorMap->getBits()) - 1; - writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, - colorMap->getDecodeHigh(0) * n); - } else if (level == psLevel2Gray || level == psLevel3Gray) { - writePS("0 1"); - } else if (colorMap->getColorSpace()->getMode() == csDeviceN) { - numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> - getAlt()->getNComps(); - for (i = 0; i < numComps; ++i) { - if (i > 0) { - writePS(" "); - } - writePS("0 1"); - } - } else { - numComps = colorMap->getNumPixelComps(); - for (i = 0; i < numComps; ++i) { - if (i > 0) { - writePS(" "); - } - writePSFmt("{0:.4g} {1:.4g}", - colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i)); - } - } - writePS("]\n"); - } else { - writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1); - } - - // data source - if (mode == psModeForm || inType3Char || preload) { - writePS(" /DataSource { pdfImStr }\n"); - } else { - writePS(" /DataSource currentfile\n"); - } - - // filters - if ((mode == psModeForm || inType3Char || preload) && - globalParams->getPSUncompressPreloadedImages()) { - s = NULL; - useLZW = useRLE = gFalse; - useCompressed = gFalse; - useASCII = gFalse; - } else { - s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, - " "); - if ((colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN || - level == psLevel2Gray || level == psLevel3Gray)) || - inlineImg || !s) { - if (globalParams->getPSLZW()) { - useLZW = gTrue; - useRLE = gFalse; - } else { - useRLE = gTrue; - useLZW = gFalse; - } - useASCII = !(mode == psModeForm || inType3Char || preload); - useCompressed = gFalse; - } else { - useLZW = useRLE = gFalse; - useASCII = str->isBinary() && - !(mode == psModeForm || inType3Char || preload); - useCompressed = gTrue; - } - } - if (useASCII) { - writePSFmt(" /ASCII{0:s}Decode filter\n", - useASCIIHex ? "Hex" : "85"); - } - if (useLZW) { - writePS(" /LZWDecode filter\n"); - } else if (useRLE) { - writePS(" /RunLengthDecode filter\n"); - } - if (useCompressed) { - writePS(s->getCString()); - } - if (s) { - delete s; - } - - if (mode == psModeForm || inType3Char || preload) { - - // end of image dictionary - writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask"); - - // get rid of the array and index - writePS("pop pop\n"); - - } else { - - // cut off inline image streams at appropriate length - if (inlineImg) { - str = new FixedLengthEncoder(str, len); - } else if (useCompressed) { - str = str->getUndecodedStream(); - } - - // recode to grayscale - if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) { - str = new GrayRecoder(str, width, height, colorMap); - - // recode DeviceN data - } else if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { - str = new DeviceNRecoder(str, width, height, colorMap); - } - - // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters - if (useLZW) { - str = new LZWEncoder(str); - } else if (useRLE) { - str = new RunLengthEncoder(str); - } - if (useASCII) { - if (useASCIIHex) { - str = new ASCIIHexEncoder(str); - } else { - str = new ASCII85Encoder(str); - } - } - - // end of image dictionary - writePS(">>\n"); -#if OPI_SUPPORT - if (opi13Nest) { - if (inlineImg) { - // this can't happen -- OPI dictionaries are in XObjects - error(errSyntaxError, -1, "OPI in inline image"); - n = 0; - } else { - // need to read the stream to count characters -- the length - // is data-dependent (because of ASCII and LZW/RunLength - // filters) - str->reset(); - n = 0; - do { - i = str->discardChars(4096); - n += i; - } while (i == 4096); - str->close(); - } - // +6/7 for "pdfIm\n" / "pdfImM\n" - // +8 for newline + trailer - n += colorMap ? 14 : 15; - writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n); - } -#endif - if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && - colorMap->getColorSpace()->getMode() == csSeparation) { - color.c[0] = gfxColorComp1; - sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); - sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); - writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", - colToDbl(cmyk.c), colToDbl(cmyk.m), - colToDbl(cmyk.y), colToDbl(cmyk.k), - sepCS->getName()); - } else { - writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); - } - - // copy the stream data - str->reset(); - while ((n = str->getBlock(buf, sizeof(buf))) > 0) { - writePSBlock(buf, n); - } - str->close(); - - // add newline and trailer to the end - writePSChar('\n'); - writePS("%-EOD-\n"); -#if OPI_SUPPORT - if (opi13Nest) { - writePS("%%EndData\n"); - } -#endif - - // delete encoders - if (useLZW || useRLE || useASCII || inlineImg) { - delete str; - } - } - - if ((maskColors && colorMap && !inlineImg) || maskStr) { - writePS("pdfImClipEnd\n"); - } -} - -// Convert color key masking to a clipping region consisting of a -// sequence of clip rectangles. -void PSOutputDev::convertColorKeyMaskToClipRects(GfxImageColorMap *colorMap, - Stream *str, - int width, int height, - int *maskColors) { - ImageStream *imgStr; - Guchar *line; - PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; - int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; - GBool emitRect, addRect, extendRect; - int numComps, i, j, x0, x1, y; - - numComps = colorMap->getNumPixelComps(); - imgStr = new ImageStream(str, width, numComps, colorMap->getBits()); - imgStr->reset(); - rects0Len = rects1Len = rectsOutLen = 0; - rectsSize = rectsOutSize = 64; - rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); - rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); - rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, - sizeof(PSOutImgClipRect)); - for (y = 0; y < height; ++y) { - if (!(line = imgStr->getLine())) { - break; - } - i = 0; - rects1Len = 0; - for (x0 = 0; x0 < width; ++x0) { - for (j = 0; j < numComps; ++j) { - if (line[x0*numComps+j] < maskColors[2*j] || - line[x0*numComps+j] > maskColors[2*j+1]) { - break; - } - } - if (j < numComps) { - break; - } - } - for (x1 = x0; x1 < width; ++x1) { - for (j = 0; j < numComps; ++j) { - if (line[x1*numComps+j] < maskColors[2*j] || - line[x1*numComps+j] > maskColors[2*j+1]) { - break; - } - } - if (j == numComps) { - break; - } - } - while (x0 < width || i < rects0Len) { - emitRect = addRect = extendRect = gFalse; - if (x0 >= width) { - emitRect = gTrue; - } else if (i >= rects0Len) { - addRect = gTrue; - } else if (rects0[i].x0 < x0) { - emitRect = gTrue; - } else if (x0 < rects0[i].x0) { - addRect = gTrue; - } else if (rects0[i].x1 == x1) { - extendRect = gTrue; - } else { - emitRect = addRect = gTrue; - } - if (emitRect) { - if (rectsOutLen == rectsOutSize) { - rectsOutSize *= 2; - rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, - sizeof(PSOutImgClipRect)); - } - rectsOut[rectsOutLen].x0 = rects0[i].x0; - rectsOut[rectsOutLen].x1 = rects0[i].x1; - rectsOut[rectsOutLen].y0 = height - y - 1; - rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1; - ++rectsOutLen; - ++i; - } - if (addRect || extendRect) { - if (rects1Len == rectsSize) { - rectsSize *= 2; - rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, - sizeof(PSOutImgClipRect)); - rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, - sizeof(PSOutImgClipRect)); - } - rects1[rects1Len].x0 = x0; - rects1[rects1Len].x1 = x1; - if (addRect) { - rects1[rects1Len].y0 = y; - } - if (extendRect) { - rects1[rects1Len].y0 = rects0[i].y0; - ++i; - } - ++rects1Len; - for (x0 = x1; x0 < width; ++x0) { - for (j = 0; j < numComps; ++j) { - if (line[x0*numComps+j] < maskColors[2*j] || - line[x0*numComps+j] > maskColors[2*j+1]) { - break; - } - } - if (j < numComps) { - break; - } - } - for (x1 = x0; x1 < width; ++x1) { - for (j = 0; j < numComps; ++j) { - if (line[x1*numComps+j] < maskColors[2*j] || - line[x1*numComps+j] > maskColors[2*j+1]) { - break; - } - } - if (j == numComps) { - break; - } - } - } - } - rectsTmp = rects0; - rects0 = rects1; - rects1 = rectsTmp; - i = rects0Len; - rects0Len = rects1Len; - rects1Len = i; - } - for (i = 0; i < rects0Len; ++i) { - if (rectsOutLen == rectsOutSize) { - rectsOutSize *= 2; - rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, - sizeof(PSOutImgClipRect)); - } - rectsOut[rectsOutLen].x0 = rects0[i].x0; - rectsOut[rectsOutLen].x1 = rects0[i].x1; - rectsOut[rectsOutLen].y0 = height - y - 1; - rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1; - ++rectsOutLen; - } - writePSFmt("{0:d} {1:d}\n", width, height); - for (i = 0; i < rectsOutLen; ++i) { - writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n", - rectsOut[i].x0, rectsOut[i].y0, - rectsOut[i].x1 - rectsOut[i].x0, - rectsOut[i].y1 - rectsOut[i].y0); - } - writePS("pop pop pdfImClip\n"); - gfree(rectsOut); - gfree(rects0); - gfree(rects1); - delete imgStr; - str->close(); -} - -// Convert an explicit mask image to a clipping region consisting of a -// sequence of clip rectangles. -void PSOutputDev::convertExplicitMaskToClipRects(Stream *maskStr, - int maskWidth, int maskHeight, - GBool maskInvert) { - ImageStream *imgStr; - Guchar *line; - PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; - int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; - GBool emitRect, addRect, extendRect; - int i, x0, x1, y, maskXor; - - imgStr = new ImageStream(maskStr, maskWidth, 1, 1); - imgStr->reset(); - rects0Len = rects1Len = rectsOutLen = 0; - rectsSize = rectsOutSize = 64; - rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); - rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); - rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, - sizeof(PSOutImgClipRect)); - maskXor = maskInvert ? 1 : 0; - for (y = 0; y < maskHeight; ++y) { - if (!(line = imgStr->getLine())) { - break; - } - i = 0; - rects1Len = 0; - for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ; - for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ; - while (x0 < maskWidth || i < rects0Len) { - emitRect = addRect = extendRect = gFalse; - if (x0 >= maskWidth) { - emitRect = gTrue; - } else if (i >= rects0Len) { - addRect = gTrue; - } else if (rects0[i].x0 < x0) { - emitRect = gTrue; - } else if (x0 < rects0[i].x0) { - addRect = gTrue; - } else if (rects0[i].x1 == x1) { - extendRect = gTrue; - } else { - emitRect = addRect = gTrue; - } - if (emitRect) { - if (rectsOutLen == rectsOutSize) { - rectsOutSize *= 2; - rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, - sizeof(PSOutImgClipRect)); - } - rectsOut[rectsOutLen].x0 = rects0[i].x0; - rectsOut[rectsOutLen].x1 = rects0[i].x1; - rectsOut[rectsOutLen].y0 = maskHeight - y - 1; - rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1; - ++rectsOutLen; - ++i; - } - if (addRect || extendRect) { - if (rects1Len == rectsSize) { - rectsSize *= 2; - rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, - sizeof(PSOutImgClipRect)); - rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, - sizeof(PSOutImgClipRect)); - } - rects1[rects1Len].x0 = x0; - rects1[rects1Len].x1 = x1; - if (addRect) { - rects1[rects1Len].y0 = y; - } - if (extendRect) { - rects1[rects1Len].y0 = rects0[i].y0; - ++i; - } - ++rects1Len; - for (x0 = x1; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ; - for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ; - } - } - rectsTmp = rects0; - rects0 = rects1; - rects1 = rectsTmp; - i = rects0Len; - rects0Len = rects1Len; - rects1Len = i; - } - for (i = 0; i < rects0Len; ++i) { - if (rectsOutLen == rectsOutSize) { - rectsOutSize *= 2; - rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, - sizeof(PSOutImgClipRect)); - } - rectsOut[rectsOutLen].x0 = rects0[i].x0; - rectsOut[rectsOutLen].x1 = rects0[i].x1; - rectsOut[rectsOutLen].y0 = maskHeight - y - 1; - rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1; - ++rectsOutLen; - } - writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight); - for (i = 0; i < rectsOutLen; ++i) { - writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n", - rectsOut[i].x0, rectsOut[i].y0, - rectsOut[i].x1 - rectsOut[i].x0, - rectsOut[i].y1 - rectsOut[i].y0); - } - writePS("pop pop pdfImClip\n"); - gfree(rectsOut); - gfree(rects0); - gfree(rects1); - delete imgStr; - maskStr->close(); -} - -//~ this doesn't currently support OPI -void PSOutputDev::doImageL3(Object *ref, GfxState *state, - GfxImageColorMap *colorMap, - GBool invert, GBool inlineImg, - Stream *str, int width, int height, int len, - int *maskColors, Stream *maskStr, - int maskWidth, int maskHeight, GBool maskInvert) { - Stream *str2; - GString *s; - int n, numComps; - GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed; - GBool maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed; - GString *maskFilters; - GfxSeparationColorSpace *sepCS; - GfxColor color; - GfxCMYK cmyk; - char buf[4096]; - int c; - int col, i; - - useASCIIHex = globalParams->getPSASCIIHex(); - useLZW = useRLE = useASCII = useCompressed = gFalse; // make gcc happy - maskUseLZW = maskUseRLE = maskUseASCII = gFalse; // make gcc happy - maskUseCompressed = gFalse; // make gcc happy - maskFilters = NULL; // make gcc happy - - // explicit masking - // -- this also converts color key masking in grayscale mode - if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { - - // mask data source - if (maskColors && colorMap && level == psLevel3Gray) { - s = NULL; - if (mode == psModeForm || inType3Char || preload) { - if (globalParams->getPSUncompressPreloadedImages()) { - maskUseLZW = maskUseRLE = gFalse; - } else if (globalParams->getPSLZW()) { - maskUseLZW = gTrue; - maskUseRLE = gFalse; - } else { - maskUseRLE = gTrue; - maskUseLZW = gFalse; - } - maskUseASCII = gFalse; - maskUseCompressed = gFalse; - } else { - if (globalParams->getPSLZW()) { - maskUseLZW = gTrue; - maskUseRLE = gFalse; - } else { - maskUseRLE = gTrue; - maskUseLZW = gFalse; - } - maskUseASCII = gTrue; - } - maskUseCompressed = gFalse; - maskWidth = width; - maskHeight = height; - maskInvert = gFalse; - } else if ((mode == psModeForm || inType3Char || preload) && - globalParams->getPSUncompressPreloadedImages()) { - s = NULL; - maskUseLZW = maskUseRLE = gFalse; - maskUseCompressed = gFalse; - maskUseASCII = gFalse; - } else { - s = maskStr->getPSFilter(3, " "); - if (!s) { - if (globalParams->getPSLZW()) { - maskUseLZW = gTrue; - maskUseRLE = gFalse; - } else { - maskUseRLE = gTrue; - maskUseLZW = gFalse; - } - maskUseASCII = !(mode == psModeForm || inType3Char || preload); - maskUseCompressed = gFalse; - } else { - maskUseLZW = maskUseRLE = gFalse; - maskUseASCII = maskStr->isBinary() && - !(mode == psModeForm || inType3Char || preload); - maskUseCompressed = gTrue; - } - } - maskFilters = new GString(); - if (maskUseASCII) { - maskFilters->appendf(" /ASCII{0:s}Decode filter\n", - useASCIIHex ? "Hex" : "85"); - } - if (maskUseLZW) { - maskFilters->append(" /LZWDecode filter\n"); - } else if (maskUseRLE) { - maskFilters->append(" /RunLengthDecode filter\n"); - } - if (maskUseCompressed) { - maskFilters->append(s); - } - if (s) { - delete s; - } - if (mode == psModeForm || inType3Char || preload) { - writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n", - ref->getRefNum(), ref->getRefGen()); - } else { - writePS("currentfile\n"); - writePS(maskFilters->getCString()); - writePS("pdfMask\n"); - - // add the ColorKeyToMask filter - if (maskColors && colorMap && level == psLevel3Gray) { - maskStr = new ColorKeyToMaskEncoder(str, width, height, colorMap, - maskColors); - } - - // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters - if (maskUseCompressed) { - maskStr = maskStr->getUndecodedStream(); - } - if (maskUseLZW) { - maskStr = new LZWEncoder(maskStr); - } else if (maskUseRLE) { - maskStr = new RunLengthEncoder(maskStr); - } - if (maskUseASCII) { - if (useASCIIHex) { - maskStr = new ASCIIHexEncoder(maskStr); - } else { - maskStr = new ASCII85Encoder(maskStr); - } - } - - // copy the stream data - maskStr->reset(); - while ((n = maskStr->getBlock(buf, sizeof(buf))) > 0) { - writePSBlock(buf, n); - } - maskStr->close(); - writePSChar('\n'); - writePS("%-EOD-\n"); - - // delete encoders - if (maskUseLZW || maskUseRLE || maskUseASCII) { - delete maskStr; - } - } - } - - // color space - if (colorMap && level != psLevel3Gray) { - dumpColorSpaceL2(state, colorMap->getColorSpace(), gFalse, gTrue, gFalse); - writePS(" setcolorspace\n"); - } - - // set up the image data - if (mode == psModeForm || inType3Char || preload) { - if (inlineImg) { - // create an array - str2 = new FixedLengthEncoder(str, len); - if (colorMap && level == psLevel3Gray) { - str2 = new GrayRecoder(str2, width, height, colorMap); - } - if (globalParams->getPSLZW()) { - str2 = new LZWEncoder(str2); - } else { - str2 = new RunLengthEncoder(str2); - } - if (useASCIIHex) { - str2 = new ASCIIHexEncoder(str2); - } else { - str2 = new ASCII85Encoder(str2); - } - str2->reset(); - col = 0; - writePS((char *)(useASCIIHex ? "[<" : "[<~")); - do { - do { - c = str2->getChar(); - } while (c == '\n' || c == '\r'); - if (c == (useASCIIHex ? '>' : '~') || c == EOF) { - break; - } - if (c == 'z') { - writePSChar((char)c); - ++col; - } else { - writePSChar((char)c); - ++col; - for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { - do { - c = str2->getChar(); - } while (c == '\n' || c == '\r'); - if (c == (useASCIIHex ? '>' : '~') || c == EOF) { - break; - } - writePSChar((char)c); - ++col; - } - } - // each line is: "<~...data...~>" - // so max data length = 255 - 6 = 249 - // chunks are 1 or 5 bytes each, so we have to stop at 245 - // but make it 240 just to be safe - if (col > 240) { - writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); - col = 0; - } - } while (c != (useASCIIHex ? '>' : '~') && c != EOF); - writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); - // add an extra entry because the LZWDecode/RunLengthDecode - // filter may read past the end - writePS("<>]\n"); - writePS("0\n"); - str2->close(); - delete str2; - } else { - // set up to use the array already created by setupImages() - writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen()); - } - } - - // explicit masking - if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { - writePS("<<\n /ImageType 3\n"); - writePS(" /InterleaveType 3\n"); - writePS(" /DataDict\n"); - } - - // image (data) dictionary - writePSFmt("<<\n /ImageType {0:d}\n", - (maskColors && colorMap && level != psLevel3Gray) ? 4 : 1); - - // color key masking - if (maskColors && colorMap && level != psLevel3Gray) { - writePS(" /MaskColor [\n"); - numComps = colorMap->getNumPixelComps(); - for (i = 0; i < 2 * numComps; i += 2) { - writePSFmt(" {0:d} {1:d}\n", maskColors[i], maskColors[i+1]); - } - writePS(" ]\n"); - } - - // width, height, matrix, bits per component - writePSFmt(" /Width {0:d}\n", width); - writePSFmt(" /Height {0:d}\n", height); - writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", - width, -height, height); - if (colorMap && level == psLevel3Gray) { - writePS(" /BitsPerComponent 8\n"); - } else { - writePSFmt(" /BitsPerComponent {0:d}\n", - colorMap ? colorMap->getBits() : 1); - } - - // decode - if (colorMap) { - writePS(" /Decode ["); - if (level == psLevel3Sep && - colorMap->getColorSpace()->getMode() == csSeparation) { - // this matches up with the code in the pdfImSep operator - n = (1 << colorMap->getBits()) - 1; - writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, - colorMap->getDecodeHigh(0) * n); - } else if (level == psLevel3Gray) { - writePS("0 1"); - } else { - numComps = colorMap->getNumPixelComps(); - for (i = 0; i < numComps; ++i) { - if (i > 0) { - writePS(" "); - } - writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i), - colorMap->getDecodeHigh(i)); - } - } - writePS("]\n"); - } else { - writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1); - } - - // data source - if (mode == psModeForm || inType3Char || preload) { - writePS(" /DataSource { pdfImStr }\n"); - } else { - writePS(" /DataSource currentfile\n"); - } - - // filters - if ((mode == psModeForm || inType3Char || preload) && - globalParams->getPSUncompressPreloadedImages()) { - s = NULL; - useLZW = useRLE = gFalse; - useCompressed = gFalse; - useASCII = gFalse; - } else { - s = str->getPSFilter(3, " "); - if ((colorMap && level == psLevel3Gray) || inlineImg || !s) { - if (globalParams->getPSLZW()) { - useLZW = gTrue; - useRLE = gFalse; - } else { - useRLE = gTrue; - useLZW = gFalse; - } - useASCII = !(mode == psModeForm || inType3Char || preload); - useCompressed = gFalse; - } else { - useLZW = useRLE = gFalse; - useASCII = str->isBinary() && - !(mode == psModeForm || inType3Char || preload); - useCompressed = gTrue; - } - } - if (useASCII) { - writePSFmt(" /ASCII{0:s}Decode filter\n", - useASCIIHex ? "Hex" : "85"); - } - if (useLZW) { - writePS(" /LZWDecode filter\n"); - } else if (useRLE) { - writePS(" /RunLengthDecode filter\n"); - } - if (useCompressed) { - writePS(s->getCString()); - } - if (s) { - delete s; - } - - // end of image (data) dictionary - writePS(">>\n"); - - // explicit masking - if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { - writePS(" /MaskDict\n"); - writePS("<<\n"); - writePS(" /ImageType 1\n"); - writePSFmt(" /Width {0:d}\n", maskWidth); - writePSFmt(" /Height {0:d}\n", maskHeight); - writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", - maskWidth, -maskHeight, maskHeight); - writePS(" /BitsPerComponent 1\n"); - writePSFmt(" /Decode [{0:d} {1:d}]\n", - maskInvert ? 1 : 0, maskInvert ? 0 : 1); - - // mask data source - if (mode == psModeForm || inType3Char || preload) { - writePS(" /DataSource {pdfMaskSrc}\n"); - writePS(maskFilters->getCString()); - } else { - writePS(" /DataSource maskStream\n"); - } - delete maskFilters; - - writePS(">>\n"); - writePS(">>\n"); - } - - if (mode == psModeForm || inType3Char || preload) { - - // image command - writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask"); - - } else { - - if (level == psLevel3Sep && colorMap && - colorMap->getColorSpace()->getMode() == csSeparation) { - color.c[0] = gfxColorComp1; - sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); - sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); - writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", - colToDbl(cmyk.c), colToDbl(cmyk.m), - colToDbl(cmyk.y), colToDbl(cmyk.k), - sepCS->getName()); - } else { - writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); - } - - } - - // get rid of the array and index - if (mode == psModeForm || inType3Char || preload) { - writePS("pop pop\n"); - - // image data - } else { - - // cut off inline image streams at appropriate length - if (inlineImg) { - str = new FixedLengthEncoder(str, len); - } else if (useCompressed) { - str = str->getUndecodedStream(); - } - - // recode to grayscale - if (colorMap && level == psLevel3Gray) { - str = new GrayRecoder(str, width, height, colorMap); - } - - // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters - if (useLZW) { - str = new LZWEncoder(str); - } else if (useRLE) { - str = new RunLengthEncoder(str); - } - if (useASCII) { - if (useASCIIHex) { - str = new ASCIIHexEncoder(str); - } else { - str = new ASCII85Encoder(str); - } - } - - // copy the stream data - str->reset(); - while ((n = str->getBlock(buf, sizeof(buf))) > 0) { - writePSBlock(buf, n); - } - str->close(); - - // add newline and trailer to the end - writePSChar('\n'); - writePS("%-EOD-\n"); - - // delete encoders - if (useLZW || useRLE || useASCII || inlineImg) { - delete str; - } - } - - // close the mask stream - if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { - if (!(mode == psModeForm || inType3Char || preload)) { - writePS("pdfMaskEnd\n"); - } - } -} - -void PSOutputDev::dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace, - GBool genXform, GBool updateColors, - GBool map01) { - switch (colorSpace->getMode()) { - case csDeviceGray: - dumpDeviceGrayColorSpace((GfxDeviceGrayColorSpace *)colorSpace, - genXform, updateColors, map01); - break; - case csCalGray: - dumpCalGrayColorSpace((GfxCalGrayColorSpace *)colorSpace, - genXform, updateColors, map01); - break; - case csDeviceRGB: - dumpDeviceRGBColorSpace((GfxDeviceRGBColorSpace *)colorSpace, - genXform, updateColors, map01); - break; - case csCalRGB: - dumpCalRGBColorSpace((GfxCalRGBColorSpace *)colorSpace, - genXform, updateColors, map01); - break; - case csDeviceCMYK: - dumpDeviceCMYKColorSpace((GfxDeviceCMYKColorSpace *)colorSpace, - genXform, updateColors, map01); - break; - case csLab: - dumpLabColorSpace((GfxLabColorSpace *)colorSpace, - genXform, updateColors, map01); - break; - case csICCBased: - dumpICCBasedColorSpace(state, (GfxICCBasedColorSpace *)colorSpace, - genXform, updateColors, map01); - break; - case csIndexed: - dumpIndexedColorSpace(state, (GfxIndexedColorSpace *)colorSpace, - genXform, updateColors, map01); - break; - case csSeparation: - dumpSeparationColorSpace(state, (GfxSeparationColorSpace *)colorSpace, - genXform, updateColors, map01); - break; - case csDeviceN: - if (level >= psLevel3) { - dumpDeviceNColorSpaceL3(state, (GfxDeviceNColorSpace *)colorSpace, - genXform, updateColors, map01); - } else { - dumpDeviceNColorSpaceL2(state, (GfxDeviceNColorSpace *)colorSpace, - genXform, updateColors, map01); - } - break; - case csPattern: - //~ unimplemented - break; - } -} - -void PSOutputDev::dumpDeviceGrayColorSpace(GfxDeviceGrayColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - writePS("/DeviceGray"); - if (genXform) { - writePS(" {}"); - } - if (updateColors) { - processColors |= psProcessBlack; - } -} - -void PSOutputDev::dumpCalGrayColorSpace(GfxCalGrayColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - writePS("[/CIEBasedA <<\n"); - writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", cs->getGamma()); - writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n", - cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); - writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", - cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); - writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", - cs->getBlackX(), cs->getBlackY(), cs->getBlackZ()); - writePS(">>]"); - if (genXform) { - writePS(" {}"); - } - if (updateColors) { - processColors |= psProcessBlack; - } -} - -void PSOutputDev::dumpDeviceRGBColorSpace(GfxDeviceRGBColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - writePS("/DeviceRGB"); - if (genXform) { - writePS(" {}"); - } - if (updateColors) { - processColors |= psProcessCMYK; - } -} - -void PSOutputDev::dumpCalRGBColorSpace(GfxCalRGBColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - writePS("[/CIEBasedABC <<\n"); - writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n", - cs->getGammaR(), cs->getGammaG(), cs->getGammaB()); - writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n", - cs->getMatrix()[0], cs->getMatrix()[1], cs->getMatrix()[2], - cs->getMatrix()[3], cs->getMatrix()[4], cs->getMatrix()[5], - cs->getMatrix()[6], cs->getMatrix()[7], cs->getMatrix()[8]); - writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", - cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); - writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", - cs->getBlackX(), cs->getBlackY(), cs->getBlackZ()); - writePS(">>]"); - if (genXform) { - writePS(" {}"); - } - if (updateColors) { - processColors |= psProcessCMYK; - } -} - -void PSOutputDev::dumpDeviceCMYKColorSpace(GfxDeviceCMYKColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - writePS("/DeviceCMYK"); - if (genXform) { - writePS(" {}"); - } - if (updateColors) { - processColors |= psProcessCMYK; - } -} - -void PSOutputDev::dumpLabColorSpace(GfxLabColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - writePS("[/CIEBasedABC <<\n"); - if (map01) { - writePS(" /RangeABC [0 1 0 1 0 1]\n"); - writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n", - (cs->getAMax() - cs->getAMin()) / 500.0, - cs->getAMin() / 500.0, - (cs->getBMax() - cs->getBMin()) / 200.0, - cs->getBMin() / 200.0); - } else { - writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n", - cs->getAMin(), cs->getAMax(), - cs->getBMin(), cs->getBMax()); - writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n"); - } - writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n"); - writePS(" /DecodeLMN\n"); - writePS(" [{dup 6 29 div ge {dup dup mul mul}\n"); - writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", - cs->getWhiteX()); - writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); - writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", - cs->getWhiteY()); - writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); - writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n", - cs->getWhiteZ()); - writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", - cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); - writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", - cs->getBlackX(), cs->getBlackY(), cs->getBlackZ()); - writePS(">>]"); - if (genXform) { - writePS(" {}"); - } - if (updateColors) { - processColors |= psProcessCMYK; - } -} - -void PSOutputDev::dumpICCBasedColorSpace(GfxState *state, - GfxICCBasedColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - // there is no transform function to the alternate color space, so - // we can use it directly - dumpColorSpaceL2(state, cs->getAlt(), genXform, updateColors, gFalse); -} - - -void PSOutputDev::dumpIndexedColorSpace(GfxState *state, - GfxIndexedColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - GfxColorSpace *baseCS; - GfxLabColorSpace *labCS; - Guchar *lookup, *p; - double x[gfxColorMaxComps], y[gfxColorMaxComps]; - double low[gfxColorMaxComps], range[gfxColorMaxComps]; - GfxColor color; - GfxCMYK cmyk; - Function *func; - int n, numComps, numAltComps; - int byte; - int i, j, k; - - baseCS = cs->getBase(); - writePS("[/Indexed "); - dumpColorSpaceL2(state, baseCS, gFalse, updateColors, gTrue); - n = cs->getIndexHigh(); - numComps = baseCS->getNComps(); - lookup = cs->getLookup(); - writePSFmt(" {0:d} <\n", n); - if (baseCS->getMode() == csDeviceN && level < psLevel3) { - func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc(); - baseCS->getDefaultRanges(low, range, cs->getIndexHigh()); - if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) { - labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt(); - } else { - labCS = NULL; - } - numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps(); - p = lookup; - for (i = 0; i <= n; i += 8) { - writePS(" "); - for (j = i; j < i+8 && j <= n; ++j) { - for (k = 0; k < numComps; ++k) { - x[k] = low[k] + (*p++ / 255.0) * range[k]; - } - func->transform(x, y); - if (labCS) { - y[0] /= 100.0; - y[1] = (y[1] - labCS->getAMin()) / - (labCS->getAMax() - labCS->getAMin()); - y[2] = (y[2] - labCS->getBMin()) / - (labCS->getBMax() - labCS->getBMin()); - } - for (k = 0; k < numAltComps; ++k) { - byte = (int)(y[k] * 255 + 0.5); - if (byte < 0) { - byte = 0; - } else if (byte > 255) { - byte = 255; - } - writePSFmt("{0:02x}", byte); - } - if (updateColors) { - color.c[0] = dblToCol(j); - cs->getCMYK(&color, &cmyk, state->getRenderingIntent()); - addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), - colToDbl(cmyk.y), colToDbl(cmyk.k)); - } - } - writePS("\n"); - } - } else { - for (i = 0; i <= n; i += 8) { - writePS(" "); - for (j = i; j < i+8 && j <= n; ++j) { - for (k = 0; k < numComps; ++k) { - writePSFmt("{0:02x}", lookup[j * numComps + k]); - } - if (updateColors) { - color.c[0] = dblToCol(j); - cs->getCMYK(&color, &cmyk, state->getRenderingIntent()); - addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), - colToDbl(cmyk.y), colToDbl(cmyk.k)); - } - } - writePS("\n"); - } - } - writePS(">]"); - if (genXform) { - writePS(" {}"); - } -} - -void PSOutputDev::dumpSeparationColorSpace(GfxState *state, - GfxSeparationColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - writePS("[/Separation "); - writePSString(cs->getName()); - writePS(" "); - dumpColorSpaceL2(state, cs->getAlt(), gFalse, gFalse, gFalse); - writePS("\n"); - cvtFunction(cs->getFunc()); - writePS("]"); - if (genXform) { - writePS(" {}"); - } - if (updateColors) { - addCustomColor(state, cs); - } -} - -void PSOutputDev::dumpDeviceNColorSpaceL2(GfxState *state, - GfxDeviceNColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - dumpColorSpaceL2(state, cs->getAlt(), gFalse, updateColors, map01); - if (genXform) { - writePS(" "); - cvtFunction(cs->getTintTransformFunc()); - } -} - -void PSOutputDev::dumpDeviceNColorSpaceL3(GfxState *state, - GfxDeviceNColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01) { - GString *tint; - int i; - - writePS("[/DeviceN [\n"); - for (i = 0; i < cs->getNComps(); ++i) { - writePSString(cs->getColorantName(i)); - writePS("\n"); - } - writePS("]\n"); - if ((tint = createDeviceNTintFunc(cs))) { - writePS("/DeviceCMYK\n"); - writePS(tint->getCString()); - delete tint; - } else { - dumpColorSpaceL2(state, cs->getAlt(), gFalse, gFalse, gFalse); - writePS("\n"); - cvtFunction(cs->getTintTransformFunc()); - } - writePS("]"); - if (genXform) { - writePS(" {}"); - } - if (updateColors) { - addCustomColors(state, cs); - } -} - -// If the DeviceN color space has a Colorants dictionary, and all of -// the colorants are one of: "None", "Cyan", "Magenta", "Yellow", -// "Black", or have an entry in the Colorants dict that maps to -// DeviceCMYK, then build a new tint function; else use the existing -// tint function. -GString *PSOutputDev::createDeviceNTintFunc(GfxDeviceNColorSpace *cs) { - Object *attrs; - Object colorants, sepCSObj, funcObj, obj1; - GString *name; - Function *func; - double sepIn; - double cmyk[gfxColorMaxComps][4]; - GString *tint; - GBool first; - int i, j; - - attrs = cs->getAttrs(); - if (!attrs->isDict()) { - return NULL; - } - if (!attrs->dictLookup("Colorants", &colorants)->isDict()) { - colorants.free(); - return NULL; - } - for (i = 0; i < cs->getNComps(); ++i) { - name = cs->getColorantName(i); - if (!name->cmp("None")) { - cmyk[i][0] = cmyk[i][1] = cmyk[i][2] = cmyk[i][3] = 0; - } else if (!name->cmp("Cyan")) { - cmyk[i][1] = cmyk[i][2] = cmyk[i][3] = 0; - cmyk[i][0] = 1; - } else if (!name->cmp("Magenta")) { - cmyk[i][0] = cmyk[i][2] = cmyk[i][3] = 0; - cmyk[i][1] = 1; - } else if (!name->cmp("Yellow")) { - cmyk[i][0] = cmyk[i][1] = cmyk[i][3] = 0; - cmyk[i][2] = 1; - } else if (!name->cmp("Black")) { - cmyk[i][0] = cmyk[i][1] = cmyk[i][2] = 0; - cmyk[i][3] = 1; - } else { - colorants.dictLookup(name->getCString(), &sepCSObj); - if (!sepCSObj.isArray() || sepCSObj.arrayGetLength() != 4) { - sepCSObj.free(); - colorants.free(); - return NULL; - } - if (!sepCSObj.arrayGet(0, &obj1)->isName("Separation")) { - obj1.free(); - sepCSObj.free(); - colorants.free(); - return NULL; - } - obj1.free(); - if (!sepCSObj.arrayGet(2, &obj1)->isName("DeviceCMYK")) { - obj1.free(); - sepCSObj.free(); - colorants.free(); - return NULL; - } - obj1.free(); - sepCSObj.arrayGet(3, &funcObj); - if (!(func = Function::parse(&funcObj))) { - funcObj.free(); - sepCSObj.free(); - colorants.free(); - return NULL; - } - funcObj.free(); - if (func->getInputSize() != 1 || func->getOutputSize() != 4) { - delete func; - sepCSObj.free(); - colorants.free(); - return NULL; - } - sepIn = 1; - func->transform(&sepIn, cmyk[i]); - delete func; - sepCSObj.free(); - } - } - colorants.free(); - - tint = new GString(); - tint->append("{\n"); - for (j = 0; j < 4; ++j) { // C, M, Y, K - first = gTrue; - for (i = 0; i < cs->getNComps(); ++i) { - if (cmyk[i][j] != 0) { - tint->appendf("{0:d} index {1:.4f} mul{2:s}\n", - j + cs->getNComps() - 1 - i, cmyk[i][j], - first ? "" : " add"); - first = gFalse; - } - } - if (first) { - tint->append("0\n"); - } - } - tint->appendf("{0:d} 4 roll\n", cs->getNComps() + 4); - for (i = 0; i < cs->getNComps(); ++i) { - tint->append("pop\n"); - } - tint->append("}\n"); - - return tint; -} - -#if OPI_SUPPORT -void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) { - Object dict; - - if (globalParams->getPSOPI()) { - opiDict->lookup("2.0", &dict); - if (dict.isDict()) { - opiBegin20(state, dict.getDict()); - dict.free(); - } else { - dict.free(); - opiDict->lookup("1.3", &dict); - if (dict.isDict()) { - opiBegin13(state, dict.getDict()); - } - dict.free(); - } - } -} - -void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { - Object obj1, obj2, obj3, obj4; - double width, height, left, right, top, bottom; - int w, h; - int i; - - writePS("%%BeginOPI: 2.0\n"); - writePS("%%Distilled\n"); - - dict->lookup("F", &obj1); - if (getFileSpec(&obj1, &obj2)) { - writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString()); - obj2.free(); - } - obj1.free(); - - dict->lookup("MainImage", &obj1); - if (obj1.isString()) { - writePSFmt("%%MainImage: {0:t}\n", obj1.getString()); - } - obj1.free(); - - //~ ignoring 'Tags' entry - //~ need to use writePSString() and deal with >255-char lines - - dict->lookup("Size", &obj1); - if (obj1.isArray() && obj1.arrayGetLength() == 2) { - obj1.arrayGet(0, &obj2); - width = obj2.getNum(); - obj2.free(); - obj1.arrayGet(1, &obj2); - height = obj2.getNum(); - obj2.free(); - writePSFmt("%%ImageDimensions: {0:.6g} {1:.6g}\n", width, height); - } - obj1.free(); - - dict->lookup("CropRect", &obj1); - if (obj1.isArray() && obj1.arrayGetLength() == 4) { - obj1.arrayGet(0, &obj2); - left = obj2.getNum(); - obj2.free(); - obj1.arrayGet(1, &obj2); - top = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2, &obj2); - right = obj2.getNum(); - obj2.free(); - obj1.arrayGet(3, &obj2); - bottom = obj2.getNum(); - obj2.free(); - writePSFmt("%%ImageCropRect: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", - left, top, right, bottom); - } - obj1.free(); - - dict->lookup("Overprint", &obj1); - if (obj1.isBool()) { - writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false"); - } - obj1.free(); - - dict->lookup("Inks", &obj1); - if (obj1.isName()) { - writePSFmt("%%ImageInks: {0:s}\n", obj1.getName()); - } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) { - obj1.arrayGet(0, &obj2); - if (obj2.isName()) { - writePSFmt("%%ImageInks: {0:s} {1:d}", - obj2.getName(), (obj1.arrayGetLength() - 1) / 2); - for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) { - obj1.arrayGet(i, &obj3); - obj1.arrayGet(i+1, &obj4); - if (obj3.isString() && obj4.isNum()) { - writePS(" "); - writePSString(obj3.getString()); - writePSFmt(" {0:.4g}", obj4.getNum()); - } - obj3.free(); - obj4.free(); - } - writePS("\n"); - } - obj2.free(); - } - obj1.free(); - - writePS("gsave\n"); - - writePS("%%BeginIncludedImage\n"); - - dict->lookup("IncludedImageDimensions", &obj1); - if (obj1.isArray() && obj1.arrayGetLength() == 2) { - obj1.arrayGet(0, &obj2); - w = obj2.getInt(); - obj2.free(); - obj1.arrayGet(1, &obj2); - h = obj2.getInt(); - obj2.free(); - writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h); - } - obj1.free(); - - dict->lookup("IncludedImageQuality", &obj1); - if (obj1.isNum()) { - writePSFmt("%%IncludedImageQuality: {0:.4g}\n", obj1.getNum()); - } - obj1.free(); - - ++opi20Nest; -} - -void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { - Object obj1, obj2; - int left, right, top, bottom, samples, bits, width, height; - double c, m, y, k; - double llx, lly, ulx, uly, urx, ury, lrx, lry; - double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry; - double horiz, vert; - int i, j; - - writePS("save\n"); - writePS("/opiMatrix2 matrix currentmatrix def\n"); - writePS("opiMatrix setmatrix\n"); - - dict->lookup("F", &obj1); - if (getFileSpec(&obj1, &obj2)) { - writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString()); - obj2.free(); - } - obj1.free(); - - dict->lookup("CropRect", &obj1); - if (obj1.isArray() && obj1.arrayGetLength() == 4) { - obj1.arrayGet(0, &obj2); - left = obj2.getInt(); - obj2.free(); - obj1.arrayGet(1, &obj2); - top = obj2.getInt(); - obj2.free(); - obj1.arrayGet(2, &obj2); - right = obj2.getInt(); - obj2.free(); - obj1.arrayGet(3, &obj2); - bottom = obj2.getInt(); - obj2.free(); - writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n", - left, top, right, bottom); - } - obj1.free(); - - dict->lookup("Color", &obj1); - if (obj1.isArray() && obj1.arrayGetLength() == 5) { - obj1.arrayGet(0, &obj2); - c = obj2.getNum(); - obj2.free(); - obj1.arrayGet(1, &obj2); - m = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2, &obj2); - y = obj2.getNum(); - obj2.free(); - obj1.arrayGet(3, &obj2); - k = obj2.getNum(); - obj2.free(); - obj1.arrayGet(4, &obj2); - if (obj2.isString()) { - writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", - c, m, y, k); - writePSString(obj2.getString()); - writePS("\n"); - } - obj2.free(); - } - obj1.free(); - - dict->lookup("ColorType", &obj1); - if (obj1.isName()) { - writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName()); - } - obj1.free(); - - //~ ignores 'Comments' entry - //~ need to handle multiple lines - - dict->lookup("CropFixed", &obj1); - if (obj1.isArray()) { - obj1.arrayGet(0, &obj2); - ulx = obj2.getNum(); - obj2.free(); - obj1.arrayGet(1, &obj2); - uly = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2, &obj2); - lrx = obj2.getNum(); - obj2.free(); - obj1.arrayGet(3, &obj2); - lry = obj2.getNum(); - obj2.free(); - writePSFmt("%ALDImageCropFixed: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n", - ulx, uly, lrx, lry); - } - obj1.free(); - - dict->lookup("GrayMap", &obj1); - if (obj1.isArray()) { - writePS("%ALDImageGrayMap:"); - for (i = 0; i < obj1.arrayGetLength(); i += 16) { - if (i > 0) { - writePS("\n%%+"); - } - for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) { - obj1.arrayGet(i+j, &obj2); - writePSFmt(" {0:d}", obj2.getInt()); - obj2.free(); - } - } - writePS("\n"); - } - obj1.free(); - - dict->lookup("ID", &obj1); - if (obj1.isString()) { - writePSFmt("%ALDImageID: {0:t}\n", obj1.getString()); - } - obj1.free(); - - dict->lookup("ImageType", &obj1); - if (obj1.isArray() && obj1.arrayGetLength() == 2) { - obj1.arrayGet(0, &obj2); - samples = obj2.getInt(); - obj2.free(); - obj1.arrayGet(1, &obj2); - bits = obj2.getInt(); - obj2.free(); - writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits); - } - obj1.free(); - - dict->lookup("Overprint", &obj1); - if (obj1.isBool()) { - writePSFmt("%ALDImageOverprint: {0:s}\n", - obj1.getBool() ? "true" : "false"); - } - obj1.free(); - - dict->lookup("Position", &obj1); - if (obj1.isArray() && obj1.arrayGetLength() == 8) { - obj1.arrayGet(0, &obj2); - llx = obj2.getNum(); - obj2.free(); - obj1.arrayGet(1, &obj2); - lly = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2, &obj2); - ulx = obj2.getNum(); - obj2.free(); - obj1.arrayGet(3, &obj2); - uly = obj2.getNum(); - obj2.free(); - obj1.arrayGet(4, &obj2); - urx = obj2.getNum(); - obj2.free(); - obj1.arrayGet(5, &obj2); - ury = obj2.getNum(); - obj2.free(); - obj1.arrayGet(6, &obj2); - lrx = obj2.getNum(); - obj2.free(); - obj1.arrayGet(7, &obj2); - lry = obj2.getNum(); - obj2.free(); - opiTransform(state, llx, lly, &tllx, &tlly); - opiTransform(state, ulx, uly, &tulx, &tuly); - opiTransform(state, urx, ury, &turx, &tury); - opiTransform(state, lrx, lry, &tlrx, &tlry); - writePSFmt("%ALDImagePosition: {0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g}\n", - tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry); - obj2.free(); - } - obj1.free(); - - dict->lookup("Resolution", &obj1); - if (obj1.isArray() && obj1.arrayGetLength() == 2) { - obj1.arrayGet(0, &obj2); - horiz = obj2.getNum(); - obj2.free(); - obj1.arrayGet(1, &obj2); - vert = obj2.getNum(); - obj2.free(); - writePSFmt("%ALDImageResoution: {0:.4g} {1:.4g}\n", horiz, vert); - obj2.free(); - } - obj1.free(); - - dict->lookup("Size", &obj1); - if (obj1.isArray() && obj1.arrayGetLength() == 2) { - obj1.arrayGet(0, &obj2); - width = obj2.getInt(); - obj2.free(); - obj1.arrayGet(1, &obj2); - height = obj2.getInt(); - obj2.free(); - writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height); - } - obj1.free(); - - //~ ignoring 'Tags' entry - //~ need to use writePSString() and deal with >255-char lines - - dict->lookup("Tint", &obj1); - if (obj1.isNum()) { - writePSFmt("%ALDImageTint: {0:.4g}\n", obj1.getNum()); - } - obj1.free(); - - dict->lookup("Transparency", &obj1); - if (obj1.isBool()) { - writePSFmt("%ALDImageTransparency: {0:s}\n", - obj1.getBool() ? "true" : "false"); - } - obj1.free(); - - writePS("%%BeginObject: image\n"); - writePS("opiMatrix2 setmatrix\n"); - ++opi13Nest; -} - -// Convert PDF user space coordinates to PostScript default user space -// coordinates. This has to account for both the PDF CTM and the -// PSOutputDev page-fitting transform. -void PSOutputDev::opiTransform(GfxState *state, double x0, double y0, - double *x1, double *y1) { - double t; - - state->transform(x0, y0, x1, y1); - *x1 += tx; - *y1 += ty; - if (rotate == 90) { - t = *x1; - *x1 = -*y1; - *y1 = t; - } else if (rotate == 180) { - *x1 = -*x1; - *y1 = -*y1; - } else if (rotate == 270) { - t = *x1; - *x1 = *y1; - *y1 = -t; - } - *x1 *= xScale; - *y1 *= yScale; -} - -void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) { - Object dict; - - if (globalParams->getPSOPI()) { - opiDict->lookup("2.0", &dict); - if (dict.isDict()) { - writePS("%%EndIncludedImage\n"); - writePS("%%EndOPI\n"); - writePS("grestore\n"); - --opi20Nest; - dict.free(); - } else { - dict.free(); - opiDict->lookup("1.3", &dict); - if (dict.isDict()) { - writePS("%%EndObject\n"); - writePS("restore\n"); - --opi13Nest; - } - dict.free(); - } - } -} - -GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) { - if (fileSpec->isString()) { - fileSpec->copy(fileName); - return gTrue; - } - if (fileSpec->isDict()) { - fileSpec->dictLookup("DOS", fileName); - if (fileName->isString()) { - return gTrue; - } - fileName->free(); - fileSpec->dictLookup("Mac", fileName); - if (fileName->isString()) { - return gTrue; - } - fileName->free(); - fileSpec->dictLookup("Unix", fileName); - if (fileName->isString()) { - return gTrue; - } - fileName->free(); - fileSpec->dictLookup("F", fileName); - if (fileName->isString()) { - return gTrue; - } - fileName->free(); - } - return gFalse; -} -#endif // OPI_SUPPORT - -void PSOutputDev::type3D0(GfxState *state, double wx, double wy) { - writePSFmt("{0:.6g} {1:.6g} setcharwidth\n", wx, wy); - writePS("q\n"); - t3NeedsRestore = gTrue; - noStateChanges = gFalse; -} - -void PSOutputDev::type3D1(GfxState *state, double wx, double wy, - double llx, double lly, double urx, double ury) { - if (t3String) { - error(errSyntaxError, -1, "Multiple 'd1' operators in Type 3 CharProc"); - return; - } - t3WX = wx; - t3WY = wy; - t3LLX = llx; - t3LLY = lly; - t3URX = urx; - t3URY = ury; - t3String = new GString(); - writePS("q\n"); - t3FillColorOnly = gTrue; - t3Cacheable = gTrue; - t3NeedsRestore = gTrue; - noStateChanges = gFalse; -} - -void PSOutputDev::drawForm(Ref id) { - writePSFmt("f_{0:d}_{1:d}\n", id.num, id.gen); - noStateChanges = gFalse; -} - -void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) { - Stream *str; - char buf[4096]; - int n; - - if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) { - str = level1Stream; - } else { - str = psStream; - } - str->reset(); - while ((n = str->getBlock(buf, sizeof(buf))) > 0) { - writePSBlock(buf, n); - } - str->close(); - noStateChanges = gFalse; -} - -//~ can nextFunc be reset to 0 -- maybe at the start of each page? -//~ or maybe at the start of each color space / pattern? -void PSOutputDev::cvtFunction(Function *func) { - SampledFunction *func0; - ExponentialFunction *func2; - StitchingFunction *func3; - PostScriptFunction *func4; - int thisFunc, m, n, nSamples, i, j, k; - - switch (func->getType()) { - - case -1: // identity - writePS("{}\n"); - break; - - case 0: // sampled - func0 = (SampledFunction *)func; - thisFunc = nextFunc++; - m = func0->getInputSize(); - n = func0->getOutputSize(); - nSamples = n; - for (i = 0; i < m; ++i) { - nSamples *= func0->getSampleSize(i); - } - writePSFmt("/xpdfSamples{0:d} [\n", thisFunc); - for (i = 0; i < nSamples; ++i) { - writePSFmt("{0:.6g}\n", func0->getSamples()[i]); - } - writePS("] def\n"); - writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2*m, m, m+2); - // [e01] [efrac] x0 x1 ... xm-1 - for (i = m-1; i >= 0; --i) { - // [e01] [efrac] x0 x1 ... xi - writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add\n", - func0->getDomainMin(i), - (func0->getEncodeMax(i) - func0->getEncodeMin(i)) / - (func0->getDomainMax(i) - func0->getDomainMin(i)), - func0->getEncodeMin(i)); - // [e01] [efrac] x0 x1 ... xi-1 xi' - writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n", - func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1); - // [e01] [efrac] x0 x1 ... xi-1 xi' - writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n"); - // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi') - writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, i); - // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') - writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, 2*i+1); - // [e01] [efrac] x0 x1 ... xi-1 floor(xi') - writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+2, 2*i); - // [e01] [efrac] x0 x1 ... xi-1 - } - // [e01] [efrac] - for (i = 0; i < n; ++i) { - // [e01] [efrac] y(0) ... y(i-1) - for (j = 0; j < (1<> k) & 1)); - for (k = m - 2; k >= 0; --k) { - writePSFmt("{0:d} mul {1:d} index {2:d} get add\n", - func0->getSampleSize(k), - i + j + 3, - 2 * k + ((j >> k) & 1)); - } - if (n > 1) { - writePSFmt("{0:d} mul {1:d} add ", n, i); - } - writePS("get\n"); - } - // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1) - for (j = 0; j < m; ++j) { - // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1) - for (k = 0; k < (1 << (m - j)); k += 2) { - // [e01] [efrac] y(0) ... y(i-1) <2^(m-j)-k s values> - writePSFmt("{0:d} index {1:d} get dup\n", - i + k/2 + (1 << (m-j)) - k, j); - writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n"); - writePSFmt("{0:d} 1 roll\n", k/2 + (1 << (m-j)) - k - 1); - } - // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1) - } - // [e01] [efrac] y(0) ... y(i-1) s - writePSFmt("{0:.6g} mul {1:.6g} add\n", - func0->getDecodeMax(i) - func0->getDecodeMin(i), - func0->getDecodeMin(i)); - writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", - func0->getRangeMin(i), func0->getRangeMin(i), - func0->getRangeMax(i), func0->getRangeMax(i)); - // [e01] [efrac] y(0) ... y(i-1) y(i) - } - // [e01] [efrac] y(0) ... y(n-1) - writePSFmt("{0:d} {1:d} roll pop pop }}\n", n+2, n); - break; - - case 2: // exponential - func2 = (ExponentialFunction *)func; - n = func2->getOutputSize(); - writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", - func2->getDomainMin(0), func2->getDomainMin(0), - func2->getDomainMax(0), func2->getDomainMax(0)); - // x - for (i = 0; i < n; ++i) { - // x y(0) .. y(i-1) - writePSFmt("{0:d} index {1:.6g} exp {2:.6g} mul {3:.6g} add\n", - i, func2->getE(), func2->getC1()[i] - func2->getC0()[i], - func2->getC0()[i]); - if (func2->getHasRange()) { - writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", - func2->getRangeMin(i), func2->getRangeMin(i), - func2->getRangeMax(i), func2->getRangeMax(i)); - } - } - // x y(0) .. y(n-1) - writePSFmt("{0:d} {1:d} roll pop }}\n", n+1, n); - break; - - case 3: // stitching - func3 = (StitchingFunction *)func; - thisFunc = nextFunc++; - for (i = 0; i < func3->getNumFuncs(); ++i) { - cvtFunction(func3->getFunc(i)); - writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc, i); - } - writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", - func3->getDomainMin(0), func3->getDomainMin(0), - func3->getDomainMax(0), func3->getDomainMax(0)); - for (i = 0; i < func3->getNumFuncs() - 1; ++i) { - writePSFmt("dup {0:.6g} lt {{ {1:.6g} sub {2:.6g} mul {3:.6g} add xpdfFunc{4:d}_{5:d} }} {{\n", - func3->getBounds()[i+1], - func3->getBounds()[i], - func3->getScale()[i], - func3->getEncode()[2*i], - thisFunc, i); - } - writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add xpdfFunc{3:d}_{4:d}\n", - func3->getBounds()[i], - func3->getScale()[i], - func3->getEncode()[2*i], - thisFunc, i); - for (i = 0; i < func3->getNumFuncs() - 1; ++i) { - writePS("} ifelse\n"); - } - writePS("}\n"); - break; - - case 4: // PostScript - func4 = (PostScriptFunction *)func; - writePS(func4->getCodeString()->getCString()); - writePS("\n"); - break; - } -} - -void PSOutputDev::writePSChar(char c) { - if (t3String) { - t3String->append(c); - } else { - (*outputFunc)(outputStream, &c, 1); - } -} - -void PSOutputDev::writePSBlock(char *s, int len) { - if (t3String) { - t3String->append(s, len); - } else { - (*outputFunc)(outputStream, s, len); - } -} - -void PSOutputDev::writePS(const char *s) { - if (t3String) { - t3String->append(s); - } else { - (*outputFunc)(outputStream, s, (int)strlen(s)); - } -} - -void PSOutputDev::writePSFmt(const char *fmt, ...) { - va_list args; - GString *buf; - - va_start(args, fmt); - if (t3String) { - t3String->appendfv((char *)fmt, args); - } else { - buf = GString::formatv((char *)fmt, args); - (*outputFunc)(outputStream, buf->getCString(), buf->getLength()); - delete buf; - } - va_end(args); -} - -void PSOutputDev::writePSString(GString *s) { - Guchar *p; - int n, line; - char buf[8]; - - writePSChar('('); - line = 1; - for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) { - if (line >= 64) { - writePSChar('\\'); - writePSChar('\n'); - line = 0; - } - if (*p == '(' || *p == ')' || *p == '\\') { - writePSChar('\\'); - writePSChar((char)*p); - line += 2; - } else if (*p < 0x20 || *p >= 0x80) { - sprintf(buf, "\\%03o", *p); - writePS(buf); - line += 4; - } else { - writePSChar((char)*p); - ++line; - } - } - writePSChar(')'); -} - -void PSOutputDev::writePSName(const char *s) { - const char *p; - char c; - - p = s; - while ((c = *p++)) { - if (c <= (char)0x20 || c >= (char)0x7f || - c == '(' || c == ')' || c == '<' || c == '>' || - c == '[' || c == ']' || c == '{' || c == '}' || - c == '/' || c == '%') { - writePSFmt("#{0:02x}", c & 0xff); - } else { - writePSChar(c); - } - } -} - -GString *PSOutputDev::filterPSName(GString *name) { - GString *name2; - char buf[8]; - int i; - char c; - - name2 = new GString(); - - // ghostscript chokes on names that begin with out-of-limits - // numbers, e.g., 1e4foo is handled correctly (as a name), but - // 1e999foo generates a limitcheck error - c = name->getChar(0); - if (c >= '0' && c <= '9') { - name2->append('f'); - } - - for (i = 0; i < name->getLength(); ++i) { - c = name->getChar(i); - if (c <= (char)0x20 || c >= (char)0x7f || - c == '(' || c == ')' || c == '<' || c == '>' || - c == '[' || c == ']' || c == '{' || c == '}' || - c == '/' || c == '%') { - sprintf(buf, "#%02x", c & 0xff); - name2->append(buf); - } else { - name2->append(c); - } - } - return name2; -} - -// Write a DSC-compliant . -void PSOutputDev::writePSTextLine(GString *s) { - TextString *ts; - Unicode *u; - int i, j; - int c; - - // - DSC comments must be printable ASCII; control chars and - // backslashes have to be escaped (we do cheap Unicode-to-ASCII - // conversion by simply ignoring the high byte) - // - lines are limited to 255 chars (we limit to 200 here to allow - // for the keyword, which was emitted by the caller) - // - lines that start with a left paren are treated as - // instead of , so we escape a leading paren - ts = new TextString(s); - u = ts->getUnicode(); - for (i = 0, j = 0; i < ts->getLength() && j < 200; ++i) { - c = u[i] & 0xff; - if (c == '\\') { - writePS("\\\\"); - j += 2; - } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) { - writePSFmt("\\{0:03o}", c); - j += 4; - } else { - writePSChar((char)c); - ++j; - } - } - writePS("\n"); - delete ts; -} diff --git a/test/bug-hunting/cve/CVE-2019-10019/PSOutputDev.h b/test/bug-hunting/cve/CVE-2019-10019/PSOutputDev.h deleted file mode 100644 index acd4eaf6355..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10019/PSOutputDev.h +++ /dev/null @@ -1,539 +0,0 @@ -//======================================================================== -// -// PSOutputDev.h -// -// Copyright 1996-2003 Glyph & Cog, LLC -// -//======================================================================== - -#ifndef PSOUTPUTDEV_H -#define PSOUTPUTDEV_H - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include -#include "config.h" -#include "Object.h" -#include "GlobalParams.h" -#include "OutputDev.h" - -class GHash; -class PDFDoc; -class XRef; -class Function; -class GfxPath; -class GfxFont; -class GfxColorSpace; -class GfxDeviceGrayColorSpace; -class GfxCalGrayColorSpace; -class GfxDeviceRGBColorSpace; -class GfxCalRGBColorSpace; -class GfxDeviceCMYKColorSpace; -class GfxLabColorSpace; -class GfxICCBasedColorSpace; -class GfxIndexedColorSpace; -class GfxSeparationColorSpace; -class GfxDeviceNColorSpace; -class PDFRectangle; -class PSOutCustomColor; -class PSOutputDev; -class PSFontFileInfo; - -//------------------------------------------------------------------------ -// PSOutputDev -//------------------------------------------------------------------------ - -enum PSOutMode { - psModePS, - psModeEPS, - psModeForm -}; - -enum PSFileType { - psFile, // write to file - psPipe, // write to pipe - psStdout, // write to stdout - psGeneric // write to a generic stream -}; - -enum PSOutCustomCodeLocation { - psOutCustomDocSetup, - psOutCustomPageSetup -}; - -typedef void (*PSOutputFunc)(void *stream, const char *data, int len); - -typedef GString *(*PSOutCustomCodeCbk)(PSOutputDev *psOut, - PSOutCustomCodeLocation loc, int n, - void *data); - -class PSOutputDev : public OutputDev { -public: - - // Open a PostScript output file, and write the prolog. - PSOutputDev(char *fileName, PDFDoc *docA, - int firstPageA, int lastPageA, PSOutMode modeA, - int imgLLXA = 0, int imgLLYA = 0, - int imgURXA = 0, int imgURYA = 0, - GBool manualCtrlA = gFalse, - PSOutCustomCodeCbk customCodeCbkA = NULL, - void *customCodeCbkDataA = NULL, - GBool honorUserUnitA = gFalse); - - // Open a PSOutputDev that will write to a generic stream. - PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, - PDFDoc *docA, - int firstPageA, int lastPageA, PSOutMode modeA, - int imgLLXA = 0, int imgLLYA = 0, - int imgURXA = 0, int imgURYA = 0, - GBool manualCtrlA = gFalse, - PSOutCustomCodeCbk customCodeCbkA = NULL, - void *customCodeCbkDataA = NULL, - GBool honorUserUnitA = gFalse); - - // Destructor -- writes the trailer and closes the file. - virtual ~PSOutputDev(); - - // Check if file was successfully created. - virtual GBool isOk() { - return ok; - } - - // Returns false if there have been any errors on the output stream. - GBool checkIO(); - - //---- get info about output device - - // Does this device use upside-down coordinates? - // (Upside-down means (0,0) is the top left corner of the page.) - virtual GBool upsideDown() { - return gFalse; - } - - // Does this device use drawChar() or drawString()? - virtual GBool useDrawChar() { - return gFalse; - } - - // Does this device use tilingPatternFill()? If this returns false, - // tiling pattern fills will be reduced to a series of other drawing - // operations. - virtual GBool useTilingPatternFill() { - return gTrue; - } - - // Does this device use functionShadedFill(), axialShadedFill(), and - // radialShadedFill()? If this returns false, these shaded fills - // will be reduced to a series of other drawing operations. - virtual GBool useShadedFills() - { - return level == psLevel2 || level == psLevel2Sep || - level == psLevel3 || level == psLevel3Sep; - } - - // Does this device use drawForm()? If this returns false, - // form-type XObjects will be interpreted (i.e., unrolled). - virtual GBool useDrawForm() { - return preload; - } - - // Does this device use beginType3Char/endType3Char? Otherwise, - // text in Type 3 fonts will be drawn with drawChar/drawString. - virtual GBool interpretType3Chars() { - return gFalse; - } - - //----- header/trailer (used only if manualCtrl is true) - - // Write the document-level header. - void writeHeader(PDFRectangle *mediaBox, PDFRectangle *cropBox, - int pageRotate); - - // Write the Xpdf procset. - void writeXpdfProcset(); - - // Write the document-level setup. - void writeDocSetup(Catalog *catalog); - - // Write the trailer for the current page. - void writePageTrailer(); - - // Write the document trailer. - void writeTrailer(); - - //----- initialization and control - - // Check to see if a page slice should be displayed. If this - // returns false, the page display is aborted. Typically, an - // OutputDev will use some alternate means to display the page - // before returning false. - virtual GBool checkPageSlice(Page *page, double hDPI, double vDPI, - int rotate, GBool useMediaBox, GBool crop, - int sliceX, int sliceY, int sliceW, int sliceH, - GBool printing, - GBool (*abortCheckCbk)(void *data) = NULL, - void *abortCheckCbkData = NULL); - - // Start a page. - virtual void startPage(int pageNum, GfxState *state); - - // End a page. - virtual void endPage(); - - //----- save/restore graphics state - virtual void saveState(GfxState *state); - virtual void restoreState(GfxState *state); - - //----- update graphics state - virtual void updateCTM(GfxState *state, double m11, double m12, - double m21, double m22, double m31, double m32); - virtual void updateLineDash(GfxState *state); - virtual void updateFlatness(GfxState *state); - virtual void updateLineJoin(GfxState *state); - virtual void updateLineCap(GfxState *state); - virtual void updateMiterLimit(GfxState *state); - virtual void updateLineWidth(GfxState *state); - virtual void updateFillColorSpace(GfxState *state); - virtual void updateStrokeColorSpace(GfxState *state); - virtual void updateFillColor(GfxState *state); - virtual void updateStrokeColor(GfxState *state); - virtual void updateFillOverprint(GfxState *state); - virtual void updateStrokeOverprint(GfxState *state); - virtual void updateOverprintMode(GfxState *state); - virtual void updateTransfer(GfxState *state); - - //----- update text state - virtual void updateFont(GfxState *state); - virtual void updateTextMat(GfxState *state); - virtual void updateCharSpace(GfxState *state); - virtual void updateRender(GfxState *state); - virtual void updateRise(GfxState *state); - virtual void updateWordSpace(GfxState *state); - virtual void updateHorizScaling(GfxState *state); - virtual void updateTextPos(GfxState *state); - virtual void updateTextShift(GfxState *state, double shift); - virtual void saveTextPos(GfxState *state); - virtual void restoreTextPos(GfxState *state); - - //----- path painting - virtual void stroke(GfxState *state); - virtual void fill(GfxState *state); - virtual void eoFill(GfxState *state); - virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef, - int paintType, int tilingType, Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep); - virtual GBool functionShadedFill(GfxState *state, - GfxFunctionShading *shading); - virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading); - virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading); - - //----- path clipping - virtual void clip(GfxState *state); - virtual void eoClip(GfxState *state); - virtual void clipToStrokePath(GfxState *state); - - //----- text drawing - virtual void drawString(GfxState *state, GString *s); - virtual void endTextObject(GfxState *state); - - //----- image drawing - virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool inlineImg, GBool interpolate); - virtual void drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - int *maskColors, GBool inlineImg, GBool interpolate); - virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - Stream *maskStr, int maskWidth, int maskHeight, - GBool maskInvert, GBool interpolate); - -#if OPI_SUPPORT - //----- OPI functions - virtual void opiBegin(GfxState *state, Dict *opiDict); - virtual void opiEnd(GfxState *state, Dict *opiDict); -#endif - - //----- Type 3 font operators - virtual void type3D0(GfxState *state, double wx, double wy); - virtual void type3D1(GfxState *state, double wx, double wy, - double llx, double lly, double urx, double ury); - - //----- form XObjects - virtual void drawForm(Ref ref); - - //----- PostScript XObjects - virtual void psXObject(Stream *psStream, Stream *level1Stream); - - //----- miscellaneous - void setImageableArea(int imgLLXA, int imgLLYA, int imgURXA, int imgURYA) - { - imgLLX = imgLLXA; imgLLY = imgLLYA; imgURX = imgURXA; imgURY = imgURYA; - } - void setOffset(double x, double y) - { - tx0 = x; ty0 = y; - } - void setScale(double x, double y) - { - xScale0 = x; yScale0 = y; - } - void setRotate(int rotateA) - { - rotate0 = rotateA; - } - void setClip(double llx, double lly, double urx, double ury) - { - clipLLX0 = llx; clipLLY0 = lly; clipURX0 = urx; clipURY0 = ury; - } - void setUnderlayCbk(void (*cbk)(PSOutputDev *psOut, void *data), - void *data) - { - underlayCbk = cbk; underlayCbkData = data; - } - void setOverlayCbk(void (*cbk)(PSOutputDev *psOut, void *data), - void *data) - { - overlayCbk = cbk; overlayCbkData = data; - } - - void writePSChar(char c); - void writePSBlock(char *s, int len); - void writePS(const char *s); - void writePSFmt(const char *fmt, ...); - void writePSString(GString *s); - void writePSName(const char *s); - -private: - - void init(PSOutputFunc outputFuncA, void *outputStreamA, - PSFileType fileTypeA, PDFDoc *docA, - int firstPageA, int lastPageA, PSOutMode modeA, - int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, - GBool manualCtrlA, GBool honorUserUnitA); - GBool checkIfPageNeedsToBeRasterized(int pg); - void setupResources(Dict *resDict); - void setupFonts(Dict *resDict); - void setupFont(GfxFont *font, Dict *parentResDict); - PSFontFileInfo *setupEmbeddedType1Font(GfxFont *font, Ref *id); - PSFontFileInfo *setupExternalType1Font(GfxFont *font, GString *fileName); - PSFontFileInfo *setupEmbeddedType1CFont(GfxFont *font, Ref *id); - PSFontFileInfo *setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id); - PSFontFileInfo *setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id); - PSFontFileInfo *setupExternalTrueTypeFont(GfxFont *font, GString *fileName, - int fontNum); - PSFontFileInfo *setupEmbeddedCIDType0Font(GfxFont *font, Ref *id); - PSFontFileInfo *setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, - GBool needVerticalMetrics); - PSFontFileInfo *setupExternalCIDTrueTypeFont(GfxFont *font, - GString *fileName, - int fontNum, - GBool needVerticalMetrics); - PSFontFileInfo *setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id); - PSFontFileInfo *setupExternalOpenTypeCFFFont(GfxFont *font, - GString *fileName); - PSFontFileInfo *setupType3Font(GfxFont *font, Dict *parentResDict); - GString *makePSFontName(GfxFont *font, Ref *id); - GString *fixType1Font(GString *font, int length1, int length2); - GBool splitType1PFA(Guchar *font, int fontSize, - int length1, int length2, - GString *textSection, GString *binSection); - GBool splitType1PFB(Guchar *font, int fontSize, - GString *textSection, GString *binSection); - GString *asciiHexDecodeType1EexecSection(GString *in); - GBool fixType1EexecSection(GString *binSection, GString *out); - GString *copyType1PFA(Guchar *font, int fontSize); - GString *copyType1PFB(Guchar *font, int fontSize); - void renameType1Font(GString *font, GString *name); - void setupDefaultFont(); - void setupImages(Dict *resDict); - void setupImage(Ref id, Stream *str, GBool mask, Array *colorKeyMask); - void setupForms(Dict *resDict); - void setupForm(Object *strRef, Object *strObj); - void addProcessColor(double c, double m, double y, double k); - void addCustomColor(GfxState *state, GfxSeparationColorSpace *sepCS); - void addCustomColors(GfxState *state, GfxDeviceNColorSpace *devnCS); - void tilingPatternFillL1(GfxState *state, Gfx *gfx, Object *strRef, - int paintType, int tilingType, Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep); - void tilingPatternFillL2(GfxState *state, Gfx *gfx, Object *strRef, - int paintType, int tilingType, Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep); - void doPath(GfxPath *path); - void doImageL1(Object *ref, GfxState *state, - GfxImageColorMap *colorMap, - GBool invert, GBool inlineImg, - Stream *str, int width, int height, int len); - void doImageL1Sep(GfxState *state, GfxImageColorMap *colorMap, - GBool invert, GBool inlineImg, - Stream *str, int width, int height, int len); - void doImageL2(Object *ref, GfxState *state, - GfxImageColorMap *colorMap, - GBool invert, GBool inlineImg, - Stream *str, int width, int height, int len, - int *maskColors, Stream *maskStr, - int maskWidth, int maskHeight, GBool maskInvert); - void convertColorKeyMaskToClipRects(GfxImageColorMap *colorMap, - Stream *str, - int width, int height, - int *maskColors); - void convertExplicitMaskToClipRects(Stream *maskStr, - int maskWidth, int maskHeight, - GBool maskInvert); - void doImageL3(Object *ref, GfxState *state, - GfxImageColorMap *colorMap, - GBool invert, GBool inlineImg, - Stream *str, int width, int height, int len, - int *maskColors, Stream *maskStr, - int maskWidth, int maskHeight, GBool maskInvert); - void dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace, - GBool genXform, GBool updateColors, - GBool map01); - void dumpDeviceGrayColorSpace(GfxDeviceGrayColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - void dumpCalGrayColorSpace(GfxCalGrayColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - void dumpDeviceRGBColorSpace(GfxDeviceRGBColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - void dumpCalRGBColorSpace(GfxCalRGBColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - void dumpDeviceCMYKColorSpace(GfxDeviceCMYKColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - void dumpLabColorSpace(GfxLabColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - void dumpICCBasedColorSpace(GfxState *state, GfxICCBasedColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - void dumpIndexedColorSpace(GfxState *state, - GfxIndexedColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - void dumpSeparationColorSpace(GfxState *state, - GfxSeparationColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - void dumpDeviceNColorSpaceL2(GfxState *state, GfxDeviceNColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - void dumpDeviceNColorSpaceL3(GfxState *state, GfxDeviceNColorSpace *cs, - GBool genXform, GBool updateColors, - GBool map01); - GString *createDeviceNTintFunc(GfxDeviceNColorSpace *cs); -#if OPI_SUPPORT - void opiBegin20(GfxState *state, Dict *dict); - void opiBegin13(GfxState *state, Dict *dict); - void opiTransform(GfxState *state, double x0, double y0, - double *x1, double *y1); - GBool getFileSpec(Object *fileSpec, Object *fileName); -#endif - void cvtFunction(Function *func); - GString *filterPSName(GString *name); - void writePSTextLine(GString *s); - - PSLevel level; // PostScript level - PSOutMode mode; // PostScript mode (PS, EPS, form) - int paperWidth; // width of paper, in pts - int paperHeight; // height of paper, in pts - GBool paperMatch; // true if paper size is set to match each page - int imgLLX, imgLLY, // imageable area, in pts - imgURX, imgURY; - GBool preload; // load all images into memory, and - // predefine forms - - PSOutputFunc outputFunc; - void *outputStream; - PSFileType fileType; // file / pipe / stdout - GBool manualCtrl; - int seqPage; // current sequential page number - void (*underlayCbk)(PSOutputDev *psOut, void *data); - void *underlayCbkData; - void (*overlayCbk)(PSOutputDev *psOut, void *data); - void *overlayCbkData; - GString *(*customCodeCbk)(PSOutputDev *psOut, - PSOutCustomCodeLocation loc, int n, - void *data); - void *customCodeCbkData; - GBool honorUserUnit; - - PDFDoc *doc; - XRef *xref; // the xref table for this PDF file - - int firstPage; // first output page - int lastPage; // last output page - char *rasterizePage; // boolean for each page - true if page - // needs to be rasterized - - GList *fontInfo; // info for each font [PSFontInfo] - GHash *fontFileInfo; // info for each font file [PSFontFileInfo] - Ref *imgIDs; // list of image IDs for in-memory images - int imgIDLen; // number of entries in imgIDs array - int imgIDSize; // size of imgIDs array - Ref *formIDs; // list of IDs for predefined forms - int formIDLen; // number of entries in formIDs array - int formIDSize; // size of formIDs array - char *visitedResources; // vector of resource objects already visited - GBool noStateChanges; // true if there have been no state changes - // since the last save - GList *saveStack; // "no state changes" flag for each - // pending save - int numTilingPatterns; // current number of nested tiling patterns - int nextFunc; // next unique number to use for a function - - GList *paperSizes; // list of used paper sizes, if paperMatch - // is true [PSOutPaperSize] - double tx0, ty0; // global translation - double xScale0, yScale0; // global scaling - int rotate0; // rotation angle (0, 90, 180, 270) - double clipLLX0, clipLLY0, - clipURX0, clipURY0; - double tx, ty; // global translation for current page - double xScale, yScale; // global scaling for current page - int rotate; // rotation angle for current page - double epsX1, epsY1, // EPS bounding box (unrotated) - epsX2, epsY2; - - GString *embFontList; // resource comments for embedded fonts - - int processColors; // used process colors - PSOutCustomColor // used custom colors - *customColors; - - GBool haveTextClip; // set if text has been drawn with a - // clipping render mode - - GBool inType3Char; // inside a Type 3 CharProc - GString *t3String; // Type 3 content string - double t3WX, t3WY, // Type 3 character parameters - t3LLX, t3LLY, t3URX, t3URY; - GBool t3FillColorOnly; // operators should only use the fill color - GBool t3Cacheable; // cleared if char is not cacheable - GBool t3NeedsRestore; // set if a 'q' operator was issued - -#if OPI_SUPPORT - int opi13Nest; // nesting level of OPI 1.3 objects - int opi20Nest; // nesting level of OPI 2.0 objects -#endif - - GBool ok; // set up ok? - - friend class WinPDFPrinter; -}; - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-10019/cmd.txt b/test/bug-hunting/cve/CVE-2019-10019/cmd.txt deleted file mode 100644 index 01699d98221..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10019/cmd.txt +++ /dev/null @@ -1 +0,0 @@ --DHAVE_SPLASH=1 diff --git a/test/bug-hunting/cve/CVE-2019-10019/expected.txt b/test/bug-hunting/cve/CVE-2019-10019/expected.txt deleted file mode 100644 index 920e7ee2d32..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10019/expected.txt +++ /dev/null @@ -1 +0,0 @@ -PSOutputDev.cc:4198:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-10020/Splash.cc b/test/bug-hunting/cve/CVE-2019-10020/Splash.cc deleted file mode 100644 index 77718abbe70..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10020/Splash.cc +++ /dev/null @@ -1,7184 +0,0 @@ -//======================================================================== -// -// Splash.cc -// -// Copyright 2003-2013 Glyph & Cog, LLC -// -//======================================================================== - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include -#include -#include -#include -#include "gmem.h" -#include "gmempp.h" -#include "SplashErrorCodes.h" -#include "SplashMath.h" -#include "SplashBitmap.h" -#include "SplashState.h" -#include "SplashPath.h" -#include "SplashXPath.h" -#include "SplashXPathScanner.h" -#include "SplashPattern.h" -#include "SplashScreen.h" -#include "SplashFont.h" -#include "SplashGlyphBitmap.h" -#include "Splash.h" - -// the MSVC math.h doesn't define this -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -//------------------------------------------------------------------------ - -// distance of Bezier control point from center for circle approximation -// = (4 * (sqrt(2) - 1) / 3) * r -#define bezierCircle ((SplashCoord)0.55228475) -#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475)) - -// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. -static inline Guchar div255(int x) { - return (Guchar)((x + (x >> 8) + 0x80) >> 8); -} - -// Clip x to lie in [0, 255]. -static inline Guchar clip255(int x) { - return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x; -} - -// Used by drawImage and fillImageMask to divide the target -// quadrilateral into sections. -struct ImageSection { - int y0, y1; // actual y range - int ia0, ia1; // vertex indices for edge A - int ib0, ib1; // vertex indices for edge B - SplashCoord xa0, ya0, xa1, ya1; // edge A - SplashCoord dxdya; // slope of edge A - SplashCoord xb0, yb0, xb1, yb1; // edge B - SplashCoord dxdyb; // slope of edge B -}; - -//------------------------------------------------------------------------ -// SplashPipe -//------------------------------------------------------------------------ - -#define splashPipeMaxStages 9 - -struct SplashPipe { - // source pattern - SplashPattern *pattern; - - // source alpha and color - Guchar aInput; - SplashColor cSrcVal; - - // special cases and result color - GBool noTransparency; - GBool shapeOnly; - SplashPipeResultColorCtrl resultColorCtrl; - - // non-isolated group correction - // (this is only used when Splash::composite() is called to composite - // a non-isolated group onto the backdrop) - GBool nonIsolatedGroup; - - // the "run" function - void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -}; - -SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { - splashPipeResultColorNoAlphaBlendMono, - splashPipeResultColorNoAlphaBlendMono, - splashPipeResultColorNoAlphaBlendRGB, - splashPipeResultColorNoAlphaBlendRGB -#if SPLASH_CMYK - , - splashPipeResultColorNoAlphaBlendCMYK -#endif -}; - -SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { - splashPipeResultColorAlphaNoBlendMono, - splashPipeResultColorAlphaNoBlendMono, - splashPipeResultColorAlphaNoBlendRGB, - splashPipeResultColorAlphaNoBlendRGB -#if SPLASH_CMYK - , - splashPipeResultColorAlphaNoBlendCMYK -#endif -}; - -SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { - splashPipeResultColorAlphaBlendMono, - splashPipeResultColorAlphaBlendMono, - splashPipeResultColorAlphaBlendRGB, - splashPipeResultColorAlphaBlendRGB -#if SPLASH_CMYK - , - splashPipeResultColorAlphaBlendCMYK -#endif -}; - -//------------------------------------------------------------------------ -// modified region -//------------------------------------------------------------------------ - -void Splash::clearModRegion() { - modXMin = bitmap->width; - modYMin = bitmap->height; - modXMax = -1; - modYMax = -1; -} - -inline void Splash::updateModX(int x) { - if (x < modXMin) { - modXMin = x; - } - if (x > modXMax) { - modXMax = x; - } -} - -inline void Splash::updateModY(int y) { - if (y < modYMin) { - modYMin = y; - } - if (y > modYMax) { - modYMax = y; - } -} - -//------------------------------------------------------------------------ -// pipeline -//------------------------------------------------------------------------ - -inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern, - Guchar aInput, GBool usesShape, - GBool nonIsolatedGroup) { - SplashColorMode mode; - - mode = bitmap->mode; - - pipe->pattern = NULL; - - // source color - if (pattern && pattern->isStatic()) { - pattern->getColor(0, 0, pipe->cSrcVal); - pipe->pattern = NULL; - } else { - pipe->pattern = pattern; - } - - // source alpha - pipe->aInput = aInput; - - // special cases - pipe->noTransparency = aInput == 255 && - !state->softMask && - !usesShape && - !state->inNonIsolatedGroup && - !state->inKnockoutGroup && - !nonIsolatedGroup && - state->overprintMask == 0xffffffff; - pipe->shapeOnly = aInput == 255 && - !state->softMask && - usesShape && - !state->inNonIsolatedGroup && - !state->inKnockoutGroup && - !nonIsolatedGroup && - state->overprintMask == 0xffffffff; - - // result color - if (pipe->noTransparency) { - // the !state->blendFunc case is handled separately in pipeRun - pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[mode]; - } else if (!state->blendFunc) { - pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[mode]; - } else { - pipe->resultColorCtrl = pipeResultColorAlphaBlend[mode]; - } - - // non-isolated group correction - pipe->nonIsolatedGroup = nonIsolatedGroup; - - // select the 'run' function - pipe->run = &Splash::pipeRun; - if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) { - if (mode == splashModeMono1 && !bitmap->alpha) { - pipe->run = &Splash::pipeRunSimpleMono1; - } else if (mode == splashModeMono8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunSimpleMono8; - } else if (mode == splashModeRGB8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunSimpleRGB8; - } else if (mode == splashModeBGR8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunSimpleBGR8; -#if SPLASH_CMYK - } else if (mode == splashModeCMYK8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunSimpleCMYK8; -#endif - } - } else if (!pipe->pattern && pipe->shapeOnly && !state->blendFunc) { - if (mode == splashModeMono1 && !bitmap->alpha) { - pipe->run = &Splash::pipeRunShapeMono1; - } else if (mode == splashModeMono8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunShapeMono8; - } else if (mode == splashModeRGB8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunShapeRGB8; - } else if (mode == splashModeBGR8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunShapeBGR8; -#if SPLASH_CMYK - } else if (mode == splashModeCMYK8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunShapeCMYK8; -#endif - } - } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask && - usesShape && - !(state->inNonIsolatedGroup && groupBackBitmap->alpha) && - !state->inKnockoutGroup && - !state->blendFunc && !pipe->nonIsolatedGroup) { - if (mode == splashModeMono1 && !bitmap->alpha) { - pipe->run = &Splash::pipeRunAAMono1; - } else if (mode == splashModeMono8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunAAMono8; - } else if (mode == splashModeRGB8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunAARGB8; - } else if (mode == splashModeBGR8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunAABGR8; -#if SPLASH_CMYK - } else if (mode == splashModeCMYK8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunAACMYK8; -#endif - } - } -} - -// general case -void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar *shapePtr2; - Guchar shape, aSrc, aDest, alphaI, alphaIm1, alpha0, aResult; - SplashColor cSrc, cDest, cBlend; - Guchar shapeVal, cResult0, cResult1, cResult2, cResult3; - int cSrcStride, shapeStride, x, lastX, t; - SplashColorPtr destColorPtr; - Guchar destColorMask; - Guchar *destAlphaPtr; - SplashColorPtr color0Ptr; - Guchar color0Mask; - Guchar *alpha0Ptr; - SplashColorPtr softMaskPtr; -#if SPLASH_CMYK - SplashColor cSrc2, cDest2; -#endif - - if (cSrcPtr && !pipe->pattern) { - cSrcStride = bitmapComps; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - - if (shapePtr) { - shapePtr2 = shapePtr; - shapeStride = 1; - for (; x0 <= x1; ++x0) { - if (*shapePtr2) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr2; - } - } else { - shapeVal = 0xff; - shapePtr2 = &shapeVal; - shapeStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - if (bitmap->mode == splashModeMono1) { - destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - destColorMask = (Guchar)(0x80 >> (x0 & 7)); - } else { - destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps]; - destColorMask = 0; // make gcc happy - } - if (bitmap->alpha) { - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - } else { - destAlphaPtr = NULL; - } - if (state->softMask) { - softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0]; - } else { - softMaskPtr = NULL; - } - if (state->inKnockoutGroup) { - if (bitmap->mode == splashModeMono1) { - color0Ptr = - &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + - ((groupBackX + x0) >> 3)]; - color0Mask = (Guchar)(0x80 >> ((groupBackX + x0) & 7)); - } else { - color0Ptr = - &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + - (groupBackX + x0) * bitmapComps]; - color0Mask = 0; // make gcc happy - } - } else { - color0Ptr = NULL; - color0Mask = 0; // make gcc happy - } - if (state->inNonIsolatedGroup && groupBackBitmap->alpha) { - alpha0Ptr = - &groupBackBitmap->alpha[(groupBackY + y) - * groupBackBitmap->alphaRowSize + - (groupBackX + x0)]; - } else { - alpha0Ptr = NULL; - } - - for (x = x0; x <= x1; ++x) { - - //----- shape - - shape = *shapePtr2; - if (!shape) { - if (bitmap->mode == splashModeMono1) { - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - } else { - destColorPtr += bitmapComps; - } - if (destAlphaPtr) { - ++destAlphaPtr; - } - if (softMaskPtr) { - ++softMaskPtr; - } - if (color0Ptr) { - if (bitmap->mode == splashModeMono1) { - color0Ptr += color0Mask & 1; - color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1)); - } else { - color0Ptr += bitmapComps; - } - } - if (alpha0Ptr) { - ++alpha0Ptr; - } - cSrcPtr += cSrcStride; - shapePtr2 += shapeStride; - continue; - } - lastX = x; - - //----- source color - - // static pattern: handled in pipeInit - // fixed color: handled in pipeInit - - // dynamic pattern - if (pipe->pattern) { - pipe->pattern->getColor(x, y, pipe->cSrcVal); - } - - cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy - - if (pipe->noTransparency && !state->blendFunc) { - - //----- result color - - switch (bitmap->mode) { - case splashModeMono1: - case splashModeMono8: - cResult0 = state->grayTransfer[cSrcPtr[0]]; - break; - case splashModeRGB8: - case splashModeBGR8: - cResult0 = state->rgbTransferR[cSrcPtr[0]]; - cResult1 = state->rgbTransferG[cSrcPtr[1]]; - cResult2 = state->rgbTransferB[cSrcPtr[2]]; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - cResult0 = state->cmykTransferC[cSrcPtr[0]]; - cResult1 = state->cmykTransferM[cSrcPtr[1]]; - cResult2 = state->cmykTransferY[cSrcPtr[2]]; - cResult3 = state->cmykTransferK[cSrcPtr[3]]; - break; -#endif - } - aResult = 255; - - } else { // if (noTransparency && !blendFunc) - - //----- read destination pixel - // (or backdrop color, for knockout groups) - - if (color0Ptr) { - - switch (bitmap->mode) { - case splashModeMono1: - cDest[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00; - color0Ptr += color0Mask & 1; - color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1)); - break; - case splashModeMono8: - cDest[0] = *color0Ptr++; - break; - case splashModeRGB8: - cDest[0] = color0Ptr[0]; - cDest[1] = color0Ptr[1]; - cDest[2] = color0Ptr[2]; - color0Ptr += 3; - break; - case splashModeBGR8: - cDest[2] = color0Ptr[0]; - cDest[1] = color0Ptr[1]; - cDest[0] = color0Ptr[2]; - color0Ptr += 3; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - cDest[0] = color0Ptr[0]; - cDest[1] = color0Ptr[1]; - cDest[2] = color0Ptr[2]; - cDest[3] = color0Ptr[3]; - color0Ptr += 4; - break; -#endif - } - - } else { - - switch (bitmap->mode) { - case splashModeMono1: - cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00; - break; - case splashModeMono8: - cDest[0] = *destColorPtr; - break; - case splashModeRGB8: - cDest[0] = destColorPtr[0]; - cDest[1] = destColorPtr[1]; - cDest[2] = destColorPtr[2]; - break; - case splashModeBGR8: - cDest[0] = destColorPtr[2]; - cDest[1] = destColorPtr[1]; - cDest[2] = destColorPtr[0]; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - cDest[0] = destColorPtr[0]; - cDest[1] = destColorPtr[1]; - cDest[2] = destColorPtr[2]; - cDest[3] = destColorPtr[3]; - break; -#endif - } - - } - - if (destAlphaPtr) { - aDest = *destAlphaPtr; - } else { - aDest = 0xff; - } - - //----- read source color; handle overprint - - switch (bitmap->mode) { - case splashModeMono1: - case splashModeMono8: - cSrc[0] = state->grayTransfer[cSrcPtr[0]]; - break; - case splashModeRGB8: - case splashModeBGR8: - cSrc[0] = state->rgbTransferR[cSrcPtr[0]]; - cSrc[1] = state->rgbTransferG[cSrcPtr[1]]; - cSrc[2] = state->rgbTransferB[cSrcPtr[2]]; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - if (state->overprintMask & 0x01) { - cSrc[0] = state->cmykTransferC[cSrcPtr[0]]; - } else { - cSrc[0] = div255(aDest * cDest[0]); - } - if (state->overprintMask & 0x02) { - cSrc[1] = state->cmykTransferM[cSrcPtr[1]]; - } else { - cSrc[1] = div255(aDest * cDest[1]); - } - if (state->overprintMask & 0x04) { - cSrc[2] = state->cmykTransferY[cSrcPtr[2]]; - } else { - cSrc[2] = div255(aDest * cDest[2]); - } - if (state->overprintMask & 0x08) { - cSrc[3] = state->cmykTransferK[cSrcPtr[3]]; - } else { - cSrc[3] = div255(aDest * cDest[3]); - } - break; -#endif - } - - //----- source alpha - - if (softMaskPtr) { - if (shapePtr) { - aSrc = div255(div255(pipe->aInput * *softMaskPtr++) * shape); - } else { - aSrc = div255(pipe->aInput * *softMaskPtr++); - } - } else if (shapePtr) { - aSrc = div255(pipe->aInput * shape); - } else { - aSrc = pipe->aInput; - } - - //----- non-isolated group correction - - if (pipe->nonIsolatedGroup) { - // This path is only used when Splash::composite() is called to - // composite a non-isolated group onto the backdrop. In this - // case, shape is the source (group) alpha. - t = (aDest * 255) / shape - aDest; - switch (bitmap->mode) { -#if SPLASH_CMYK - case splashModeCMYK8: - cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255); -#endif - case splashModeRGB8: - case splashModeBGR8: - cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255); - cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255); - case splashModeMono1: - case splashModeMono8: - cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255); - break; - } - } - - //----- blend function - - if (state->blendFunc) { -#if SPLASH_CMYK - if (bitmap->mode == splashModeCMYK8) { - // convert colors to additive - cSrc2[0] = (Guchar)(0xff - cSrc[0]); - cSrc2[1] = (Guchar)(0xff - cSrc[1]); - cSrc2[2] = (Guchar)(0xff - cSrc[2]); - cSrc2[3] = (Guchar)(0xff - cSrc[3]); - cDest2[0] = (Guchar)(0xff - cDest[0]); - cDest2[1] = (Guchar)(0xff - cDest[1]); - cDest2[2] = (Guchar)(0xff - cDest[2]); - cDest2[3] = (Guchar)(0xff - cDest[3]); - (*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode); - // convert result back to subtractive - cBlend[0] = (Guchar)(0xff - cBlend[0]); - cBlend[1] = (Guchar)(0xff - cBlend[1]); - cBlend[2] = (Guchar)(0xff - cBlend[2]); - cBlend[3] = (Guchar)(0xff - cBlend[3]); - } else -#endif - (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode); - } - - //----- result alpha and non-isolated group element correction - - // alphaI = alpha_i - // alphaIm1 = alpha_(i-1) - - if (pipe->noTransparency) { - alphaI = alphaIm1 = aResult = 255; - } else if (alpha0Ptr) { - if (color0Ptr) { - // non-isolated, knockout - aResult = aSrc; - alpha0 = *alpha0Ptr++; - alphaI = (Guchar)(aSrc + alpha0 - div255(aSrc * alpha0)); - alphaIm1 = alpha0; - } else { - // non-isolated, non-knockout - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alpha0 = *alpha0Ptr++; - alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0)); - alphaIm1 = (Guchar)(alpha0 + aDest - div255(alpha0 * aDest)); - } - } else { - if (color0Ptr) { - // isolated, knockout - aResult = aSrc; - alphaI = aSrc; - alphaIm1 = 0; - } else { - // isolated, non-knockout - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - alphaIm1 = aDest; - } - } - - //----- result color - - switch (pipe->resultColorCtrl) { - - case splashPipeResultColorNoAlphaBlendMono: - cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); - break; - case splashPipeResultColorNoAlphaBlendRGB: - cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); - cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]); - cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]); - break; -#if SPLASH_CMYK - case splashPipeResultColorNoAlphaBlendCMYK: - cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); - cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]); - cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]); - cResult3 = div255((255 - aDest) * cSrc[3] + aDest * cBlend[3]); - break; -#endif - - case splashPipeResultColorAlphaNoBlendMono: - if (alphaI == 0) { - cResult0 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) - / alphaI); - } - break; - case splashPipeResultColorAlphaNoBlendRGB: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) - / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) - / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) - / alphaI); - } - break; -#if SPLASH_CMYK - case splashPipeResultColorAlphaNoBlendCMYK: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - cResult3 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) - / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) - / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) - / alphaI); - cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + aSrc * cSrc[3]) - / alphaI); - } - break; -#endif - - case splashPipeResultColorAlphaBlendMono: - if (alphaI == 0) { - cResult0 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + - aSrc * ((255 - alphaIm1) * cSrc[0] + - alphaIm1 * cBlend[0]) / 255) - / alphaI); - } - break; - case splashPipeResultColorAlphaBlendRGB: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + - aSrc * ((255 - alphaIm1) * cSrc[0] + - alphaIm1 * cBlend[0]) / 255) - / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + - aSrc * ((255 - alphaIm1) * cSrc[1] + - alphaIm1 * cBlend[1]) / 255) - / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + - aSrc * ((255 - alphaIm1) * cSrc[2] + - alphaIm1 * cBlend[2]) / 255) - / alphaI); - } - break; -#if SPLASH_CMYK - case splashPipeResultColorAlphaBlendCMYK: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - cResult3 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + - aSrc * ((255 - alphaIm1) * cSrc[0] + - alphaIm1 * cBlend[0]) / 255) - / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + - aSrc * ((255 - alphaIm1) * cSrc[1] + - alphaIm1 * cBlend[1]) / 255) - / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + - aSrc * ((255 - alphaIm1) * cSrc[2] + - alphaIm1 * cBlend[2]) / 255) - / alphaI); - cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + - aSrc * ((255 - alphaIm1) * cSrc[3] + - alphaIm1 * cBlend[3]) / 255) - / alphaI); - } - break; -#endif - } - - } // if (noTransparency && !blendFunc) - - //----- write destination pixel - - switch (bitmap->mode) { - case splashModeMono1: - if (state->screen->test(x, y, cResult0)) { - *destColorPtr |= destColorMask; - } else { - *destColorPtr &= (Guchar)~destColorMask; - } - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - break; - case splashModeMono8: - *destColorPtr++ = cResult0; - break; - case splashModeRGB8: - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr += 3; - break; - case splashModeBGR8: - destColorPtr[0] = cResult2; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult0; - destColorPtr += 3; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr[3] = cResult3; - destColorPtr += 4; - break; -#endif - } - if (destAlphaPtr) { - *destAlphaPtr++ = aResult; - } - - cSrcPtr += cSrcStride; - shapePtr2 += shapeStride; - } // for (x ...) - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeMono1 && !bitmap->alpha) { -void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar cResult0; - SplashColorPtr destColorPtr; - Guchar destColorMask; - SplashScreenCursor screenCursor; - int cSrcStride, x; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModX(x1); - updateModY(y); - - destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - destColorMask = (Guchar)(0x80 >> (x0 & 7)); - - screenCursor = state->screen->getTestCursor(y); - - for (x = x0; x <= x1; ++x) { - - //----- write destination pixel - cResult0 = state->grayTransfer[cSrcPtr[0]]; - if (state->screen->testWithCursor(screenCursor, x, cResult0)) { - *destColorPtr |= destColorMask; - } else { - *destColorPtr &= (Guchar)~destColorMask; - } - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - - cSrcPtr += cSrcStride; - } -} - -// special case: -// !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeMono8 && bitmap->alpha) { -void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModX(x1); - updateModY(y); - - destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- write destination pixel - *destColorPtr++ = state->grayTransfer[cSrcPtr[0]]; - *destAlphaPtr++ = 255; - - cSrcPtr += cSrcStride; - } -} - -// special case: -// !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeRGB8 && bitmap->alpha) { -void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModX(x1); - updateModY(y); - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- write destination pixel - destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]]; - destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; - destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]]; - destColorPtr += 3; - *destAlphaPtr++ = 255; - - cSrcPtr += cSrcStride; - } -} - -// special case: -// !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeBGR8 && bitmap->alpha) { -void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModX(x1); - updateModY(y); - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- write destination pixel - destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]]; - destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; - destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]]; - destColorPtr += 3; - *destAlphaPtr++ = 255; - - cSrcPtr += cSrcStride; - } -} - -#if SPLASH_CMYK -// special case: -// !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeCMYK8 && bitmap->alpha) { -void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x; - - if (cSrcPtr) { - cSrcStride = 4; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModX(x1); - updateModY(y); - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- write destination pixel - destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]]; - destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]]; - destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]]; - destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]]; - destColorPtr += 4; - *destAlphaPtr++ = 255; - - cSrcPtr += cSrcStride; - } -} -#endif - - -// special case: -// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && -// bitmap->mode == splashModeMono1 && !bitmap->alpha -void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, cSrc0, cDest0, cResult0; - SplashColorPtr destColorPtr; - Guchar destColorMask; - SplashScreenCursor screenCursor; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - destColorMask = (Guchar)(0x80 >> (x0 & 7)); - - screenCursor = state->screen->getTestCursor(y); - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- source color - cSrc0 = state->grayTransfer[cSrcPtr[0]]; - - //----- source alpha - aSrc = shape; - - //----- special case for aSrc = 255 - if (aSrc == 255) { - cResult0 = cSrc0; - } else { - - //----- read destination pixel - cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; - - //----- result color - // note: aDest = alphaI = aResult = 0xff - cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0); - } - - //----- write destination pixel - if (state->screen->testWithCursor(screenCursor, x, cResult0)) { - *destColorPtr |= destColorMask; - } else { - *destColorPtr &= (Guchar)~destColorMask; - } - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && -// bitmap->mode == splashModeMono8 && bitmap->alpha -void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - ++destColorPtr; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- source color - cSrc0 = state->grayTransfer[cSrcPtr[0]]; - - //----- source alpha - aSrc = shape; - - //----- special case for aSrc = 255 - if (aSrc == 255) { - aResult = 255; - cResult0 = cSrc0; - } else { - - //----- read destination alpha - aDest = *destAlphaPtr; - - //----- special case for aDest = 0 - if (aDest == 0) { - aResult = aSrc; - cResult0 = cSrc0; - } else { - - //----- read destination pixel - cDest0 = *destColorPtr; - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - } - } - - //----- write destination pixel - *destColorPtr++ = cResult0; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && -// bitmap->mode == splashModeRGB8 && bitmap->alpha -void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2; - Guchar cDest0, cDest1, cDest2; - Guchar cResult0, cResult1, cResult2; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 3; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- source color - cSrc0 = state->rgbTransferR[cSrcPtr[0]]; - cSrc1 = state->rgbTransferG[cSrcPtr[1]]; - cSrc2 = state->rgbTransferB[cSrcPtr[2]]; - - //----- source alpha - aSrc = shape; - - //----- special case for aSrc = 255 - if (aSrc == 255) { - aResult = 255; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - } else { - - //----- read destination alpha - aDest = *destAlphaPtr; - - //----- special case for aDest = 0 - if (aDest == 0) { - aResult = aSrc; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - } else { - - //----- read destination pixel - cDest0 = destColorPtr[0]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[2]; - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - } - } - - //----- write destination pixel - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr += 3; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && -// bitmap->mode == splashModeBGR8 && bitmap->alpha -void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2; - Guchar cDest0, cDest1, cDest2; - Guchar cResult0, cResult1, cResult2; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 3; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- source color - cSrc0 = state->rgbTransferR[cSrcPtr[0]]; - cSrc1 = state->rgbTransferG[cSrcPtr[1]]; - cSrc2 = state->rgbTransferB[cSrcPtr[2]]; - - //----- source alpha - aSrc = shape; - - //----- special case for aSrc = 255 - if (aSrc == 255) { - aResult = 255; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - } else { - - //----- read destination alpha - aDest = *destAlphaPtr; - - //----- special case for aDest = 0 - if (aDest == 0) { - aResult = aSrc; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - } else { - - //----- read destination pixel - cDest0 = destColorPtr[2]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[0]; - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - } - } - - //----- write destination pixel - destColorPtr[0] = cResult2; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult0; - destColorPtr += 3; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -#if SPLASH_CMYK -// special case: -// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && -// bitmap->mode == splashModeCMYK8 && bitmap->alpha -void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2, cSrc3; - Guchar cDest0, cDest1, cDest2, cDest3; - Guchar cResult0, cResult1, cResult2, cResult3; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 4; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 4; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = destColorPtr[0]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[2]; - cDest3 = destColorPtr[3]; - aDest = *destAlphaPtr; - - //----- overprint - if (state->overprintMask & 1) { - cSrc0 = state->cmykTransferC[cSrcPtr[0]]; - } else { - cSrc0 = div255(aDest * cDest0); - } - if (state->overprintMask & 2) { - cSrc1 = state->cmykTransferM[cSrcPtr[1]]; - } else { - cSrc1 = div255(aDest * cDest1); - } - if (state->overprintMask & 4) { - cSrc2 = state->cmykTransferY[cSrcPtr[2]]; - } else { - cSrc2 = div255(aDest * cDest2); - } - if (state->overprintMask & 8) { - cSrc3 = state->cmykTransferK[cSrcPtr[3]]; - } else { - cSrc3 = div255(aDest * cDest3); - } - - //----- source alpha - aSrc = shape; - - //----- special case for aSrc = 255 - if (aSrc == 255) { - aResult = 255; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - cResult3 = cSrc3; - } else { - - //----- special case for aDest = 0 - if (aDest == 0) { - aResult = aSrc; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - cResult3 = cSrc3; - } else { - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI); - } - } - - //----- write destination pixel - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr[3] = cResult3; - destColorPtr += 4; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} -#endif - - -// special case: -// !pipe->pattern && !pipe->noTransparency && !state->softMask && -// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && -// !pipe->nonIsolatedGroup && -// bitmap->mode == splashModeMono1 && !bitmap->alpha -void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, cSrc0, cDest0, cResult0; - SplashColorPtr destColorPtr; - Guchar destColorMask; - SplashScreenCursor screenCursor; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - destColorMask = (Guchar)(0x80 >> (x0 & 7)); - - screenCursor = state->screen->getTestCursor(y); - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; - - //----- source color - cSrc0 = state->grayTransfer[cSrcPtr[0]]; - - //----- source alpha - aSrc = div255(pipe->aInput * shape); - - //----- result color - // note: aDest = alphaI = aResult = 0xff - cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0); - - //----- write destination pixel - if (state->screen->testWithCursor(screenCursor, x, cResult0)) { - *destColorPtr |= destColorMask; - } else { - *destColorPtr &= (Guchar)~destColorMask; - } - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && !pipe->noTransparency && !state->softMask && -// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && -// !pipe->nonIsolatedGroup && -// bitmap->mode == splashModeMono8 && bitmap->alpha -void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - ++destColorPtr; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = *destColorPtr; - aDest = *destAlphaPtr; - - //----- source color - cSrc0 = state->grayTransfer[cSrcPtr[0]]; - - //----- source alpha - aSrc = div255(pipe->aInput * shape); - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - if (alphaI == 0) { - cResult0 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - } - - //----- write destination pixel - *destColorPtr++ = cResult0; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && !pipe->noTransparency && !state->softMask && -// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && -// !pipe->nonIsolatedGroup && -// bitmap->mode == splashModeRGB8 && bitmap->alpha -void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2; - Guchar cDest0, cDest1, cDest2; - Guchar cResult0, cResult1, cResult2; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 3; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = destColorPtr[0]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[2]; - aDest = *destAlphaPtr; - - //----- source color - cSrc0 = state->rgbTransferR[cSrcPtr[0]]; - cSrc1 = state->rgbTransferG[cSrcPtr[1]]; - cSrc2 = state->rgbTransferB[cSrcPtr[2]]; - - //----- source alpha - aSrc = div255(pipe->aInput * shape); - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - } - - //----- write destination pixel - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr += 3; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && !pipe->noTransparency && !state->softMask && -// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && -// !pipe->nonIsolatedGroup && -// bitmap->mode == splashModeBGR8 && bitmap->alpha -void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2; - Guchar cDest0, cDest1, cDest2; - Guchar cResult0, cResult1, cResult2; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 3; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = destColorPtr[2]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[0]; - aDest = *destAlphaPtr; - - //----- source color - cSrc0 = state->rgbTransferR[cSrcPtr[0]]; - cSrc1 = state->rgbTransferG[cSrcPtr[1]]; - cSrc2 = state->rgbTransferB[cSrcPtr[2]]; - - //----- source alpha - aSrc = div255(pipe->aInput * shape); - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - } - - //----- write destination pixel - destColorPtr[0] = cResult2; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult0; - destColorPtr += 3; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -#if SPLASH_CMYK -// special case: -// !pipe->pattern && !pipe->noTransparency && !state->softMask && -// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && -// !pipe->nonIsolatedGroup && -// bitmap->mode == splashModeCMYK8 && bitmap->alpha -void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2, cSrc3; - Guchar cDest0, cDest1, cDest2, cDest3; - Guchar cResult0, cResult1, cResult2, cResult3; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 4; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 4; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = destColorPtr[0]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[2]; - cDest3 = destColorPtr[3]; - aDest = *destAlphaPtr; - - //----- overprint - if (state->overprintMask & 1) { - cSrc0 = state->cmykTransferC[cSrcPtr[0]]; - } else { - cSrc0 = div255(aDest * cDest0); - } - if (state->overprintMask & 2) { - cSrc1 = state->cmykTransferM[cSrcPtr[1]]; - } else { - cSrc1 = div255(aDest * cDest1); - } - if (state->overprintMask & 4) { - cSrc2 = state->cmykTransferY[cSrcPtr[2]]; - } else { - cSrc2 = div255(aDest * cDest2); - } - if (state->overprintMask & 8) { - cSrc3 = state->cmykTransferK[cSrcPtr[3]]; - } else { - cSrc3 = div255(aDest * cDest3); - } - - //----- source alpha - aSrc = div255(pipe->aInput * shape); - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - cResult3 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI); - } - - //----- write destination pixel - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr[3] = cResult3; - destColorPtr += 4; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} -#endif - - -//------------------------------------------------------------------------ - -// Transform a point from user space to device space. -inline void Splash::transform(SplashCoord *matrix, - SplashCoord xi, SplashCoord yi, - SplashCoord *xo, SplashCoord *yo) { - // [ m[0] m[1] 0 ] - // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] - // [ m[4] m[5] 1 ] - *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; - *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; -} - -//------------------------------------------------------------------------ -// Splash -//------------------------------------------------------------------------ - -Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, - SplashScreenParams *screenParams) { - bitmap = bitmapA; - bitmapComps = splashColorModeNComps[bitmap->mode]; - vectorAntialias = vectorAntialiasA; - inShading = gFalse; - state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, - screenParams); - scanBuf = (Guchar *)gmalloc(bitmap->width); - if (bitmap->mode == splashModeMono1) { - scanBuf2 = (Guchar *)gmalloc(bitmap->width); - } else { - scanBuf2 = NULL; - } - groupBackBitmap = NULL; - minLineWidth = 0; - clearModRegion(); - debugMode = gFalse; -} - -Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, - SplashScreen *screenA) { - bitmap = bitmapA; - bitmapComps = splashColorModeNComps[bitmap->mode]; - vectorAntialias = vectorAntialiasA; - inShading = gFalse; - state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, - screenA); - scanBuf = (Guchar *)gmalloc(bitmap->width); - if (bitmap->mode == splashModeMono1) { - scanBuf2 = (Guchar *)gmalloc(bitmap->width); - } else { - scanBuf2 = NULL; - } - groupBackBitmap = NULL; - minLineWidth = 0; - clearModRegion(); - debugMode = gFalse; -} - -Splash::~Splash() { - while (state->next) { - restoreState(); - } - delete state; - gfree(scanBuf); - gfree(scanBuf2); -} - -//------------------------------------------------------------------------ -// state read -//------------------------------------------------------------------------ - -SplashCoord *Splash::getMatrix() { - return state->matrix; -} - -SplashPattern *Splash::getStrokePattern() { - return state->strokePattern; -} - -SplashPattern *Splash::getFillPattern() { - return state->fillPattern; -} - -SplashScreen *Splash::getScreen() { - return state->screen; -} - -SplashBlendFunc Splash::getBlendFunc() { - return state->blendFunc; -} - -SplashCoord Splash::getStrokeAlpha() { - return state->strokeAlpha; -} - -SplashCoord Splash::getFillAlpha() { - return state->fillAlpha; -} - -SplashCoord Splash::getLineWidth() { - return state->lineWidth; -} - -int Splash::getLineCap() { - return state->lineCap; -} - -int Splash::getLineJoin() { - return state->lineJoin; -} - -SplashCoord Splash::getMiterLimit() { - return state->miterLimit; -} - -SplashCoord Splash::getFlatness() { - return state->flatness; -} - -SplashCoord *Splash::getLineDash() { - return state->lineDash; -} - -int Splash::getLineDashLength() { - return state->lineDashLength; -} - -SplashCoord Splash::getLineDashPhase() { - return state->lineDashPhase; -} - -SplashStrokeAdjustMode Splash::getStrokeAdjust() { - return state->strokeAdjust; -} - -SplashClip *Splash::getClip() { - return state->clip; -} - -SplashBitmap *Splash::getSoftMask() { - return state->softMask; -} - -GBool Splash::getInNonIsolatedGroup() { - return state->inNonIsolatedGroup; -} - -GBool Splash::getInKnockoutGroup() { - return state->inKnockoutGroup; -} - -//------------------------------------------------------------------------ -// state write -//------------------------------------------------------------------------ - -void Splash::setMatrix(SplashCoord *matrix) { - memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord)); -} - -void Splash::setStrokePattern(SplashPattern *strokePattern) { - state->setStrokePattern(strokePattern); -} - -void Splash::setFillPattern(SplashPattern *fillPattern) { - state->setFillPattern(fillPattern); -} - -void Splash::setScreen(SplashScreen *screen) { - state->setScreen(screen); -} - -void Splash::setBlendFunc(SplashBlendFunc func) { - state->blendFunc = func; -} - -void Splash::setStrokeAlpha(SplashCoord alpha) { - state->strokeAlpha = alpha; -} - -void Splash::setFillAlpha(SplashCoord alpha) { - state->fillAlpha = alpha; -} - -void Splash::setLineWidth(SplashCoord lineWidth) { - state->lineWidth = lineWidth; -} - -void Splash::setLineCap(int lineCap) { - if (lineCap >= 0 && lineCap <= 2) { - state->lineCap = lineCap; - } else { - state->lineCap = 0; - } -} - -void Splash::setLineJoin(int lineJoin) { - if (lineJoin >= 0 && lineJoin <= 2) { - state->lineJoin = lineJoin; - } else { - state->lineJoin = 0; - } -} - -void Splash::setMiterLimit(SplashCoord miterLimit) { - state->miterLimit = miterLimit; -} - -void Splash::setFlatness(SplashCoord flatness) { - if (flatness < 1) { - state->flatness = 1; - } else { - state->flatness = flatness; - } -} - -void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength, - SplashCoord lineDashPhase) { - state->setLineDash(lineDash, lineDashLength, lineDashPhase); -} - -void Splash::setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust) { - state->strokeAdjust = strokeAdjust; -} - -void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1) { - state->clipResetToRect(x0, y0, x1, y1); -} - -SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1) { - return state->clipToRect(x0, y0, x1, y1); -} - -SplashError Splash::clipToPath(SplashPath *path, GBool eo) { - return state->clipToPath(path, eo); -} - -void Splash::setSoftMask(SplashBitmap *softMask) { - state->setSoftMask(softMask); -} - -void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA, - int groupBackXA, int groupBackYA, - GBool nonIsolated, GBool knockout) { - groupBackBitmap = groupBackBitmapA; - groupBackX = groupBackXA; - groupBackY = groupBackYA; - state->inNonIsolatedGroup = nonIsolated; - state->inKnockoutGroup = knockout; -} - -void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue, - Guchar *gray) { - state->setTransfer(red, green, blue, gray); -} - -void Splash::setOverprintMask(Guint overprintMask) { - state->overprintMask = overprintMask; -} - - -void Splash::setEnablePathSimplification(GBool en) { - state->enablePathSimplification = en; -} - -//------------------------------------------------------------------------ -// state save/restore -//------------------------------------------------------------------------ - -void Splash::saveState() { - SplashState *newState; - - newState = state->copy(); - newState->next = state; - state = newState; -} - -SplashError Splash::restoreState() { - SplashState *oldState; - - if (!state->next) { - return splashErrNoSave; - } - oldState = state; - state = state->next; - delete oldState; - return splashOk; -} - -//------------------------------------------------------------------------ -// drawing operations -//------------------------------------------------------------------------ - -void Splash::clear(SplashColorPtr color, Guchar alpha) { - SplashColorPtr row, p; - Guchar mono; - int x, y; - - switch (bitmap->mode) { - case splashModeMono1: - mono = (color[0] & 0x80) ? 0xff : 0x00; - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - mono, -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, mono, bitmap->rowSize * bitmap->height); - } - break; - case splashModeMono8: - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - color[0], -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); - } - break; - case splashModeRGB8: - if (color[0] == color[1] && color[1] == color[2]) { - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - color[0], -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); - } - } else { - row = bitmap->data; - for (y = 0; y < bitmap->height; ++y) { - p = row; - for (x = 0; x < bitmap->width; ++x) { - *p++ = color[0]; - *p++ = color[1]; - *p++ = color[2]; - } - row += bitmap->rowSize; - } - } - break; - case splashModeBGR8: - if (color[0] == color[1] && color[1] == color[2]) { - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - color[0], -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); - } - } else { - row = bitmap->data; - for (y = 0; y < bitmap->height; ++y) { - p = row; - for (x = 0; x < bitmap->width; ++x) { - *p++ = color[2]; - *p++ = color[1]; - *p++ = color[0]; - } - row += bitmap->rowSize; - } - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) { - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - color[0], -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); - } - } else { - row = bitmap->data; - for (y = 0; y < bitmap->height; ++y) { - p = row; - for (x = 0; x < bitmap->width; ++x) { - *p++ = color[0]; - *p++ = color[1]; - *p++ = color[2]; - *p++ = color[3]; - } - row += bitmap->rowSize; - } - } - break; -#endif - } - - if (bitmap->alpha) { - memset(bitmap->alpha, alpha, bitmap->alphaRowSize * bitmap->height); - } - - updateModX(0); - updateModY(0); - updateModX(bitmap->width - 1); - updateModY(bitmap->height - 1); -} - -SplashError Splash::stroke(SplashPath *path) { - SplashPath *path2, *dPath; - SplashCoord t0, t1, t2, t3, w, w2, lineDashMax, lineDashTotal; - int lineCap, lineJoin, i; - - if (debugMode) { - printf("stroke [dash:%d] [width:%.2f]:\n", - state->lineDashLength, (double)state->lineWidth); - dumpPath(path); - } - opClipRes = splashClipAllOutside; - if (path->length == 0) { - return splashErrEmptyPath; - } - path2 = flattenPath(path, state->matrix, state->flatness); - - // Compute an approximation of the transformed line width. - // Given a CTM of [m0 m1], - // [m2 m3] - // if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else - // use min{|m1|,|m2|}. - // This handles the common cases -- [s 0 ] and [0 s] -- - // [0 +/-s] [+/-s 0] - // well, and still does something reasonable for the uncommon - // case transforms. - t0 = splashAbs(state->matrix[0]); - t1 = splashAbs(state->matrix[1]); - t2 = splashAbs(state->matrix[2]); - t3 = splashAbs(state->matrix[3]); - if (t0 * t3 >= t1 * t2) { - w = (t0 < t3) ? t0 : t3; - } else { - w = (t1 < t2) ? t1 : t2; - } - w2 = w * state->lineWidth; - - // construct the dashed path - if (state->lineDashLength > 0) { - - // check the maximum transformed dash element length (using the - // same approximation as for line width) -- if it's less than 0.1 - // pixel, don't apply the dash pattern; this avoids a huge - // performance/memory hit with PDF files that use absurd dash - // patterns like [0.0007 0.0003] - lineDashTotal = 0; - lineDashMax = 0; - for (i = 0; i < state->lineDashLength; ++i) { - lineDashTotal += state->lineDash[i]; - if (state->lineDash[i] > lineDashMax) { - lineDashMax = state->lineDash[i]; - } - } - // Acrobat simply draws nothing if the dash array is [0] - if (lineDashTotal == 0) { - delete path2; - return splashOk; - } - if (w * lineDashMax > 0.1) { - - dPath = makeDashedPath(path2); - delete path2; - path2 = dPath; - if (path2->length == 0) { - delete path2; - return splashErrEmptyPath; - } - } - } - - // round caps on narrow lines look bad, and can't be - // stroke-adjusted, so use projecting caps instead (but we can't do - // this if there are zero-length dashes or segments, because those - // turn into round dots) - lineCap = state->lineCap; - lineJoin = state->lineJoin; - if (state->strokeAdjust == splashStrokeAdjustCAD && - w2 < 3.5) { - if (lineCap == splashLineCapRound && - !state->lineDashContainsZeroLengthDashes() && - !path->containsZeroLengthSubpaths()) { - lineCap = splashLineCapProjecting; - } - if (lineJoin == splashLineJoinRound) { - lineJoin = splashLineJoinBevel; - } - } - - // if there is a min line width set, and the transformed line width - // is smaller, use the min line width - if (w > 0 && w2 < minLineWidth) { - strokeWide(path2, minLineWidth / w, splashLineCapButt, splashLineJoinBevel); - } else if (bitmap->mode == splashModeMono1 || !vectorAntialias) { - // in monochrome mode or if antialiasing is disabled, use 0-width - // lines for any transformed line width <= 1 -- lines less than 1 - // pixel wide look too fat without antialiasing - if (w2 < 1.001) { - strokeNarrow(path2); - } else { - strokeWide(path2, state->lineWidth, lineCap, lineJoin); - } - } else { - // in gray and color modes, only use 0-width lines if the line - // width is explicitly set to 0 - if (state->lineWidth == 0) { - strokeNarrow(path2); - } else { - strokeWide(path2, state->lineWidth, lineCap, lineJoin); - } - } - - delete path2; - return splashOk; -} - -void Splash::strokeNarrow(SplashPath *path) { - SplashPipe pipe; - SplashXPath *xPath; - SplashXPathSeg *seg; - int x0, x1, y0, y1, xa, xb, y; - SplashCoord dxdy; - SplashClipResult clipRes; - int nClipRes[3]; - int i; - - nClipRes[0] = nClipRes[1] = nClipRes[2] = 0; - - xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse, - state->enablePathSimplification, - state->strokeAdjust); - - pipeInit(&pipe, state->strokePattern, - (Guchar)splashRound(state->strokeAlpha * 255), - gTrue, gFalse); - - for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { - if (seg->y0 <= seg->y1) { - y0 = splashFloor(seg->y0); - y1 = splashFloor(seg->y1); - x0 = splashFloor(seg->x0); - x1 = splashFloor(seg->x1); - } else { - y0 = splashFloor(seg->y1); - y1 = splashFloor(seg->y0); - x0 = splashFloor(seg->x1); - x1 = splashFloor(seg->x0); - } - if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, - x0 <= x1 ? x1 : x0, y1, - state->strokeAdjust)) - != splashClipAllOutside) { - if (y0 == y1) { - if (x0 <= x1) { - drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); - } else { - drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside); - } - } else { - dxdy = seg->dxdy; - y = state->clip->getYMinI(state->strokeAdjust); - if (y0 < y) { - y0 = y; - x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy); - } - y = state->clip->getYMaxI(state->strokeAdjust); - if (y1 > y) { - y1 = y; - x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy); - } - if (x0 <= x1) { - xa = x0; - for (y = y0; y <= y1; ++y) { - if (y < y1) { - xb = splashFloor(seg->x0 + - ((SplashCoord)y + 1 - seg->y0) * dxdy); - } else { - xb = x1 + 1; - } - if (xa == xb) { - drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); - } else { - drawStrokeSpan(&pipe, xa, xb - 1, y, - clipRes == splashClipAllInside); - } - xa = xb; - } - } else { - xa = x0; - for (y = y0; y <= y1; ++y) { - if (y < y1) { - xb = splashFloor(seg->x0 + - ((SplashCoord)y + 1 - seg->y0) * dxdy); - } else { - xb = x1 - 1; - } - if (xa == xb) { - drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); - } else { - drawStrokeSpan(&pipe, xb + 1, xa, y, - clipRes == splashClipAllInside); - } - xa = xb; - } - } - } - } - ++nClipRes[clipRes]; - } - if (nClipRes[splashClipPartial] || - (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) { - opClipRes = splashClipPartial; - } else if (nClipRes[splashClipAllInside]) { - opClipRes = splashClipAllInside; - } else { - opClipRes = splashClipAllOutside; - } - - delete xPath; -} - -void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, - GBool noClip) { - int x; - - x = state->clip->getXMinI(state->strokeAdjust); - if (x > x0) { - x0 = x; - } - x = state->clip->getXMaxI(state->strokeAdjust); - if (x < x1) { - x1 = x; - } - if (x0 > x1) { - return; - } - for (x = x0; x <= x1; ++x) { - scanBuf[x] = 0xff; - } - if (!noClip) { - if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) { - return; - } - } - (this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, NULL); -} - -void Splash::strokeWide(SplashPath *path, SplashCoord w, - int lineCap, int lineJoin) { - SplashPath *path2; - - path2 = makeStrokePath(path, w, lineCap, lineJoin, gFalse); - fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha); - delete path2; -} - -SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness) { - SplashPath *fPath; - SplashCoord flatness2; - Guchar flag; - int i; - - fPath = new SplashPath(); -#if USE_FIXEDPOINT - flatness2 = flatness; -#else - flatness2 = flatness * flatness; -#endif - i = 0; - while (i < path->length) { - flag = path->flags[i]; - if (flag & splashPathFirst) { - fPath->moveTo(path->pts[i].x, path->pts[i].y); - ++i; - } else { - if (flag & splashPathCurve) { - flattenCurve(path->pts[i-1].x, path->pts[i-1].y, - path->pts[i ].x, path->pts[i ].y, - path->pts[i+1].x, path->pts[i+1].y, - path->pts[i+2].x, path->pts[i+2].y, - matrix, flatness2, fPath); - i += 3; - } else { - fPath->lineTo(path->pts[i].x, path->pts[i].y); - ++i; - } - if (path->flags[i-1] & splashPathClosed) { - fPath->close(); - } - } - } - return fPath; -} - -void Splash::flattenCurve(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1, - SplashCoord x2, SplashCoord y2, - SplashCoord x3, SplashCoord y3, - SplashCoord *matrix, SplashCoord flatness2, - SplashPath *fPath) { - SplashCoord cx[splashMaxCurveSplits + 1][3]; - SplashCoord cy[splashMaxCurveSplits + 1][3]; - int cNext[splashMaxCurveSplits + 1]; - SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; - SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; - SplashCoord dx, dy, mx, my, tx, ty, d1, d2; - int p1, p2, p3; - - // initial segment - p1 = 0; - p2 = splashMaxCurveSplits; - cx[p1][0] = x0; cy[p1][0] = y0; - cx[p1][1] = x1; cy[p1][1] = y1; - cx[p1][2] = x2; cy[p1][2] = y2; - cx[p2][0] = x3; cy[p2][0] = y3; - cNext[p1] = p2; - - while (p1 < splashMaxCurveSplits) { - - // get the next segment - xl0 = cx[p1][0]; yl0 = cy[p1][0]; - xx1 = cx[p1][1]; yy1 = cy[p1][1]; - xx2 = cx[p1][2]; yy2 = cy[p1][2]; - p2 = cNext[p1]; - xr3 = cx[p2][0]; yr3 = cy[p2][0]; - - // compute the distances (in device space) from the control points - // to the midpoint of the straight line (this is a bit of a hack, - // but it's much faster than computing the actual distances to the - // line) - transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my); - transform(matrix, xx1, yy1, &tx, &ty); -#if USE_FIXEDPOINT - d1 = splashDist(tx, ty, mx, my); -#else - dx = tx - mx; - dy = ty - my; - d1 = dx*dx + dy*dy; -#endif - transform(matrix, xx2, yy2, &tx, &ty); -#if USE_FIXEDPOINT - d2 = splashDist(tx, ty, mx, my); -#else - dx = tx - mx; - dy = ty - my; - d2 = dx*dx + dy*dy; -#endif - - // if the curve is flat enough, or no more subdivisions are - // allowed, add the straight line segment - if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { - fPath->lineTo(xr3, yr3); - p1 = p2; - - // otherwise, subdivide the curve - } else { - xl1 = splashAvg(xl0, xx1); - yl1 = splashAvg(yl0, yy1); - xh = splashAvg(xx1, xx2); - yh = splashAvg(yy1, yy2); - xl2 = splashAvg(xl1, xh); - yl2 = splashAvg(yl1, yh); - xr2 = splashAvg(xx2, xr3); - yr2 = splashAvg(yy2, yr3); - xr1 = splashAvg(xh, xr2); - yr1 = splashAvg(yh, yr2); - xr0 = splashAvg(xl2, xr1); - yr0 = splashAvg(yl2, yr1); - // add the new subdivision points - p3 = (p1 + p2) / 2; - cx[p1][1] = xl1; cy[p1][1] = yl1; - cx[p1][2] = xl2; cy[p1][2] = yl2; - cNext[p1] = p3; - cx[p3][0] = xr0; cy[p3][0] = yr0; - cx[p3][1] = xr1; cy[p3][1] = yr1; - cx[p3][2] = xr2; cy[p3][2] = yr2; - cNext[p3] = p2; - } - } -} - -SplashPath *Splash::makeDashedPath(SplashPath *path) { - SplashPath *dPath; - SplashCoord lineDashTotal; - SplashCoord lineDashStartPhase, lineDashDist, segLen; - SplashCoord x0, y0, x1, y1, xa, ya; - GBool lineDashStartOn, lineDashEndOn, lineDashOn, newPath; - int lineDashStartIdx, lineDashIdx, subpathStart, nDashes; - int i, j, k; - - lineDashTotal = 0; - for (i = 0; i < state->lineDashLength; ++i) { - lineDashTotal += state->lineDash[i]; - } - // Acrobat simply draws nothing if the dash array is [0] - if (lineDashTotal == 0) { - return new SplashPath(); - } - lineDashStartPhase = state->lineDashPhase; - if (lineDashStartPhase > lineDashTotal * 2) { - i = splashFloor(lineDashStartPhase / (lineDashTotal * 2)); - lineDashStartPhase -= lineDashTotal * i * 2; - } else if (lineDashStartPhase < 0) { - i = splashCeil(-lineDashStartPhase / (lineDashTotal * 2)); - lineDashStartPhase += lineDashTotal * i * 2; - } - i = splashFloor(lineDashStartPhase / lineDashTotal); - lineDashStartPhase -= (SplashCoord)i * lineDashTotal; - lineDashStartOn = gTrue; - lineDashStartIdx = 0; - if (lineDashStartPhase > 0) { - while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { - lineDashStartOn = !lineDashStartOn; - lineDashStartPhase -= state->lineDash[lineDashStartIdx]; - if (++lineDashStartIdx == state->lineDashLength) { - lineDashStartIdx = 0; - } - } - } - - dPath = new SplashPath(); - - // process each subpath - i = 0; - while (i < path->length) { - - // find the end of the subpath - for (j = i; - j < path->length - 1 && !(path->flags[j] & splashPathLast); - ++j) ; - - // initialize the dash parameters - lineDashOn = lineDashStartOn; - lineDashEndOn = lineDashStartOn; - lineDashIdx = lineDashStartIdx; - lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; - subpathStart = dPath->length; - nDashes = 0; - - // process each segment of the subpath - newPath = gTrue; - for (k = i; k < j; ++k) { - - // grab the segment - x0 = path->pts[k].x; - y0 = path->pts[k].y; - x1 = path->pts[k+1].x; - y1 = path->pts[k+1].y; - segLen = splashDist(x0, y0, x1, y1); - - // process the segment - while (segLen > 0) { - - // Special case for zero-length dash segments: draw a very - // short -- but not zero-length -- segment. This ensures that - // we get the correct behavior with butt and projecting line - // caps. The PS/PDF specs imply that zero-length segments are - // not drawn unless the line cap is round, but Acrobat and - // Ghostscript both draw very short segments (for butt caps) - // and squares (for projecting caps). - if (lineDashDist == 0) { - if (lineDashOn) { - if (newPath) { - dPath->moveTo(x0, y0); - newPath = gFalse; - ++nDashes; - } - xa = x0 + ((SplashCoord)0.001 / segLen) * (x1 - x0); - ya = y0 + ((SplashCoord)0.001 / segLen) * (y1 - y0); - dPath->lineTo(xa, ya); - } - - } else if (lineDashDist >= segLen) { - if (lineDashOn) { - if (newPath) { - dPath->moveTo(x0, y0); - newPath = gFalse; - ++nDashes; - } - dPath->lineTo(x1, y1); - } - lineDashDist -= segLen; - segLen = 0; - - } else { - xa = x0 + (lineDashDist / segLen) * (x1 - x0); - ya = y0 + (lineDashDist / segLen) * (y1 - y0); - if (lineDashOn) { - if (newPath) { - dPath->moveTo(x0, y0); - newPath = gFalse; - ++nDashes; - } - dPath->lineTo(xa, ya); - } - x0 = xa; - y0 = ya; - segLen -= lineDashDist; - lineDashDist = 0; - } - - lineDashEndOn = lineDashOn; - - // get the next entry in the dash array - if (lineDashDist <= 0) { - lineDashOn = !lineDashOn; - if (++lineDashIdx == state->lineDashLength) { - lineDashIdx = 0; - } - lineDashDist = state->lineDash[lineDashIdx]; - newPath = gTrue; - } - } - } - - // in a closed subpath, where the dash pattern is "on" at both the - // start and end of the subpath, we need to merge the start and - // end to get a proper line join - if ((path->flags[j] & splashPathClosed) && - lineDashStartOn && - lineDashEndOn) { - if (nDashes == 1) { - dPath->close(); - } else if (nDashes > 1) { - k = subpathStart; - do { - ++k; - dPath->lineTo(dPath->pts[k].x, dPath->pts[k].y); - } while (!(dPath->flags[k] & splashPathLast)); - ++k; - memmove(&dPath->pts[subpathStart], &dPath->pts[k], - (dPath->length - k) * sizeof(SplashPathPoint)); - memmove(&dPath->flags[subpathStart], &dPath->flags[k], - (dPath->length - k) * sizeof(Guchar)); - dPath->length -= k - subpathStart; - dPath->curSubpath -= k - subpathStart; - } - } - - i = j + 1; - } - - return dPath; -} - -SplashError Splash::fill(SplashPath *path, GBool eo) { - if (debugMode) { - printf("fill [eo:%d]:\n", eo); - dumpPath(path); - } - return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha); -} - -SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, - SplashPattern *pattern, - SplashCoord alpha) { - SplashPipe pipe; - SplashPath *path2; - SplashXPath *xPath; - SplashXPathScanner *scanner; - int xMin, yMin, xMax, xMin2, xMax2, yMax, y, t; - SplashClipResult clipRes; - - if (path->length == 0) { - return splashErrEmptyPath; - } - if (pathAllOutside(path)) { - opClipRes = splashClipAllOutside; - return splashOk; - } - - path2 = tweakFillPath(path); - - xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue, - state->enablePathSimplification, - state->strokeAdjust); - if (path2 != path) { - delete path2; - } - xMin = xPath->getXMin(); - yMin = xPath->getYMin(); - xMax = xPath->getXMax(); - yMax = xPath->getYMax(); - if (xMin > xMax || yMin > yMax) { - delete xPath; - return splashOk; - } - scanner = new SplashXPathScanner(xPath, eo, yMin, yMax); - - // check clipping - if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, - state->strokeAdjust)) - != splashClipAllOutside) { - - if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { - xMin = t; - } - if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { - xMax = t; - } - if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { - yMin = t; - } - if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { - yMax = t; - } - if (xMin > xMax || yMin > yMax) { - delete scanner; - delete xPath; - return splashOk; - } - - pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255), - gTrue, gFalse); - - // draw the spans - if (vectorAntialias && !inShading) { - for (y = yMin; y <= yMax; ++y) { - scanner->getSpan(scanBuf, y, xMin, xMax, &xMin2, &xMax2); - if (xMin2 <= xMax2) { - if (clipRes != splashClipAllInside) { - state->clip->clipSpan(scanBuf, y, xMin2, xMax2, - state->strokeAdjust); - } - (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL); - } - } - } else { - for (y = yMin; y <= yMax; ++y) { - scanner->getSpanBinary(scanBuf, y, xMin, xMax, &xMin2, &xMax2); - if (xMin2 <= xMax2) { - if (clipRes != splashClipAllInside) { - state->clip->clipSpanBinary(scanBuf, y, xMin2, xMax2, - state->strokeAdjust); - } - (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL); - } - } - } - } - opClipRes = clipRes; - - delete scanner; - delete xPath; - return splashOk; -} - -// Applies various tweaks to a fill path: -// (1) add stroke adjust hints to a filled rectangle -// (2) applies a minimum width to a zero-width filled rectangle (so -// stroke adjustment works correctly -// (3) convert a degenerate fill ('moveto lineto fill' and 'moveto -// lineto closepath fill') to a minimum-width filled rectangle -// -// These tweaks only apply to paths with a single subpath. -// -// Returns either the unchanged input path or a new path (in which -// case the returned path must be deleted by the caller). -SplashPath *Splash::tweakFillPath(SplashPath *path) { - SplashPath *path2; - SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w; - int n; - - if (state->strokeAdjust == splashStrokeAdjustOff || path->hints) { - return path; - } - - n = path->getLength(); - if (!((n == 2) || - (n == 3 && - path->flags[1] == 0) || - (n == 4 && - path->flags[1] == 0 && - path->flags[2] == 0) || - (n == 5 && - path->flags[1] == 0 && - path->flags[2] == 0 && - path->flags[3] == 0))) { - return path; - } - - path2 = path; - - // degenerate fill (2 or 3 points) or rectangle of (nearly) zero - // width --> replace with a min-width rectangle and hint - if (n == 2 || - (n == 3 && (path->flags[0] & splashPathClosed)) || - (n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 && - splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) || - ((n == 4 || - (n == 5 && (path->flags[0] & splashPathClosed))) && - ((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 && - splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 && - splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 && - splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) || - (splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 && - splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 && - splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 && - splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) { - wx = state->matrix[0] + state->matrix[2]; - wy = state->matrix[1] + state->matrix[3]; - w = splashSqrt(wx*wx + wy*wy); - if (w < 0.001) { - w = 0; - } else { - // min width is 0.1 -- this constant is minWidth * sqrt(2) - w = (SplashCoord)0.1414 / w; - } - xx0 = path->pts[0].x; - yy0 = path->pts[0].y; - if (n <= 3) { - xx1 = path->pts[1].x; - yy1 = path->pts[1].y; - } else { - xx1 = path->pts[2].x; - yy1 = path->pts[2].y; - } - dx = xx1 - xx0; - dy = yy1 - yy0; - d = splashSqrt(dx * dx + dy * dy); - if (d < 0.001) { - d = 0; - } else { - d = w / d; - } - dx *= d; - dy *= d; - path2 = new SplashPath(); - path2->moveTo(xx0 + dy, yy0 - dx); - path2->lineTo(xx1 + dy, yy1 - dx); - path2->lineTo(xx1 - dy, yy1 + dx); - path2->lineTo(xx0 - dy, yy0 + dx); - path2->close(gTrue); - path2->addStrokeAdjustHint(0, 2, 0, 4); - path2->addStrokeAdjustHint(1, 3, 0, 4); - - // unclosed rectangle --> close and hint - } else if (n == 4 && !(path->flags[0] & splashPathClosed)) { - path2->close(gTrue); - path2->addStrokeAdjustHint(0, 2, 0, 4); - path2->addStrokeAdjustHint(1, 3, 0, 4); - - // closed rectangle --> hint - } else if (n == 5 && (path->flags[0] & splashPathClosed)) { - path2->addStrokeAdjustHint(0, 2, 0, 4); - path2->addStrokeAdjustHint(1, 3, 0, 4); - } - - return path2; -} - -GBool Splash::pathAllOutside(SplashPath *path) { - SplashCoord xMin1, yMin1, xMax1, yMax1; - SplashCoord xMin2, yMin2, xMax2, yMax2; - SplashCoord x, y; - int xMinI, yMinI, xMaxI, yMaxI; - int i; - - xMin1 = xMax1 = path->pts[0].x; - yMin1 = yMax1 = path->pts[0].y; - for (i = 1; i < path->length; ++i) { - if (path->pts[i].x < xMin1) { - xMin1 = path->pts[i].x; - } else if (path->pts[i].x > xMax1) { - xMax1 = path->pts[i].x; - } - if (path->pts[i].y < yMin1) { - yMin1 = path->pts[i].y; - } else if (path->pts[i].y > yMax1) { - yMax1 = path->pts[i].y; - } - } - - transform(state->matrix, xMin1, yMin1, &x, &y); - xMin2 = xMax2 = x; - yMin2 = yMax2 = y; - transform(state->matrix, xMin1, yMax1, &x, &y); - if (x < xMin2) { - xMin2 = x; - } else if (x > xMax2) { - xMax2 = x; - } - if (y < yMin2) { - yMin2 = y; - } else if (y > yMax2) { - yMax2 = y; - } - transform(state->matrix, xMax1, yMin1, &x, &y); - if (x < xMin2) { - xMin2 = x; - } else if (x > xMax2) { - xMax2 = x; - } - if (y < yMin2) { - yMin2 = y; - } else if (y > yMax2) { - yMax2 = y; - } - transform(state->matrix, xMax1, yMax1, &x, &y); - if (x < xMin2) { - xMin2 = x; - } else if (x > xMax2) { - xMax2 = x; - } - if (y < yMin2) { - yMin2 = y; - } else if (y > yMax2) { - yMax2 = y; - } - // sanity-check the coordinates - xMinI/yMinI/xMaxI/yMaxI are - // 32-bit integers, so coords need to be < 2^31 - SplashXPath::clampCoords(&xMin2, &yMin2); - SplashXPath::clampCoords(&xMax2, &yMax2); - xMinI = splashFloor(xMin2); - yMinI = splashFloor(yMin2); - xMaxI = splashFloor(xMax2); - yMaxI = splashFloor(yMax2); - - return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI, - state->strokeAdjust) == - splashClipAllOutside; -} - -SplashError Splash::fillChar(SplashCoord x, SplashCoord y, - int c, SplashFont *font) { - SplashGlyphBitmap glyph; - SplashCoord xt, yt; - int x0, y0, xFrac, yFrac; - SplashError err; - - if (debugMode) { - printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n", - (double)x, (double)y, c, c, c); - } - transform(state->matrix, x, y, &xt, &yt); - x0 = splashFloor(xt); - xFrac = splashFloor((xt - x0) * splashFontFraction); - y0 = splashFloor(yt); - yFrac = splashFloor((yt - y0) * splashFontFraction); - if (!font->getGlyph(c, xFrac, yFrac, &glyph)) { - return splashErrNoGlyph; - } - err = fillGlyph2(x0, y0, &glyph); - if (glyph.freeData) { - gfree(glyph.data); - } - return err; -} - -SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y, - SplashGlyphBitmap *glyph) { - SplashCoord xt, yt; - int x0, y0; - - transform(state->matrix, x, y, &xt, &yt); - x0 = splashFloor(xt); - y0 = splashFloor(yt); - return fillGlyph2(x0, y0, glyph); -} - -SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) { - SplashPipe pipe; - SplashClipResult clipRes; - Guchar alpha; - Guchar *p; - int xMin, yMin, xMax, yMax; - int x, y, xg, yg, xx, t; - - xg = x0 - glyph->x; - yg = y0 - glyph->y; - xMin = xg; - xMax = xg + glyph->w - 1; - yMin = yg; - yMax = yg + glyph->h - 1; - if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, - state->strokeAdjust)) - != splashClipAllOutside) { - pipeInit(&pipe, state->fillPattern, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - if (clipRes == splashClipAllInside) { - if (glyph->aa) { - p = glyph->data; - for (y = yMin; y <= yMax; ++y) { - (this->*pipe.run)(&pipe, xMin, xMax, y, - glyph->data + (y - yMin) * glyph->w, NULL); - } - } else { - p = glyph->data; - for (y = yMin; y <= yMax; ++y) { - for (x = xMin; x <= xMax; x += 8) { - alpha = *p++; - for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { - scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00; - alpha = (Guchar)(alpha << 1); - } - } - (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); - } - } - } else { - if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { - xMin = t; - } - if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { - xMax = t; - } - if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { - yMin = t; - } - if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { - yMax = t; - } - if (xMin <= xMax && yMin <= yMax) { - if (glyph->aa) { - for (y = yMin; y <= yMax; ++y) { - p = glyph->data + (y - yg) * glyph->w + (xMin - xg); - memcpy(scanBuf + xMin, p, xMax - xMin + 1); - state->clip->clipSpan(scanBuf, y, xMin, xMax, - state->strokeAdjust); - (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); - } - } else { - for (y = yMin; y <= yMax; ++y) { - p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3) - + ((xMin - xg) >> 3); - alpha = *p++; - xx = (xMin - xg) & 7; - alpha = (Guchar)(alpha << xx); - for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) { - scanBuf[x] = (alpha & 0x80) ? 255 : 0; - alpha = (Guchar)(alpha << 1); - } - for (; x <= xMax; x += 8) { - alpha = *p++; - for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { - scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0; - alpha = (Guchar)(alpha << 1); - } - } - state->clip->clipSpanBinary(scanBuf, y, xMin, xMax, - state->strokeAdjust); - (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); - } - } - } - } - } - opClipRes = clipRes; - - return splashOk; -} - -void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax, - int *xyMinI, int *xyMaxI) { - if (state->strokeAdjust == splashStrokeAdjustOff) { - *xyMinI = splashFloor(xyMin); - *xyMaxI = splashFloor(xyMax); - if (*xyMaxI <= *xyMinI) { - *xyMaxI = *xyMinI + 1; - } - } else { - splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI, state->strokeAdjust); - } -} - -// The glyphMode flag is not currently used, but may be useful if the -// stroke adjustment behavior is changed. -SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, - int w, int h, SplashCoord *mat, - GBool glyphMode, GBool interpolate) { - SplashBitmap *scaledMask; - SplashClipResult clipRes; - GBool minorAxisZero; - SplashCoord wSize, hSize, t0, t1; - int x0, y0, x1, y1, scaledWidth, scaledHeight; - - if (debugMode) { - printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", - w, h, (double)mat[0], (double)mat[1], (double)mat[2], - (double)mat[3], (double)mat[4], (double)mat[5]); - } - - // check for singular matrix - if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { - return splashErrSingularMatrix; - } - - minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; - - // rough estimate of size of scaled mask - t0 = splashAbs(mat[0]); - t1 = splashAbs(mat[1]); - wSize = t0 > t1 ? t0 : t1; - t0 = splashAbs(mat[2]); - t1 = splashAbs(mat[3]); - hSize = t0 > t1 ? t0 : t1; - - // stream-mode upscaling -- this is slower, so we only use it if the - // upscaled mask is large (in which case clipping should remove many - // pixels) -#if USE_FIXEDPOINT - if ((wSize > 2 * w && hSize > 2 * h && (int)wSize > 1000000 / (int)hSize) || - (wSize > w && hSize > h && (int)wSize > 10000000 / (int)hSize) || - ((wSize > w || hSize > h) && (int)wSize > 25000000 / (int)hSize)) { -#else - if ((wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) || - (wSize > w && hSize > h && wSize * hSize > 10000000) || - ((wSize > w || hSize > h) && wSize * hSize > 25000000)) { - upscaleMask(src, srcData, w, h, mat, glyphMode, interpolate); -#endif - - // scaling only - } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { - getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); - getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, - interpolate); - blitMask(scaledMask, x0, y0, clipRes); - delete scaledMask; - } - - // scaling plus vertical flip - } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { - getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); - getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, - interpolate); - vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); - blitMask(scaledMask, x0, y0, clipRes); - delete scaledMask; - } - - // scaling plus horizontal flip - } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) { - getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); - getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, - interpolate); - horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); - blitMask(scaledMask, x0, y0, clipRes); - delete scaledMask; - } - - // scaling plus horizontal and vertical flips - } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) { - getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); - getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, - interpolate); - vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); - horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); - blitMask(scaledMask, x0, y0, clipRes); - delete scaledMask; - } - - // all other cases - } else { - arbitraryTransformMask(src, srcData, w, h, mat, glyphMode, interpolate); - } - - return splashOk; -} - -// The glyphMode flag is not currently used, but may be useful if the -// stroke adjustment behavior is changed. -void Splash::upscaleMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool glyphMode, - GBool interpolate) { - SplashClipResult clipRes; - SplashPipe pipe; - Guchar *unscaledImage, *p; - SplashCoord xMin, yMin, xMax, yMax, t; - SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; - SplashCoord ix, iy, sx, sy, pix0, pix1; - int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt; - - // compute the bbox of the target quadrilateral - xMin = xMax = mat[4]; - t = mat[2] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - t = mat[0] + mat[2] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - t = mat[0] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - getImageBounds(xMin, xMax, &xMinI, &xMaxI); - yMin = yMax = mat[5]; - t = mat[3] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - t = mat[1] + mat[3] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - t = mat[1] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - getImageBounds(yMin, yMax, &yMinI, &yMaxI); - - // clipping - clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes == splashClipAllOutside) { - return; - } - if (clipRes != splashClipAllInside) { - if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { - xMinI = tt; - } - if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { - xMaxI = tt; - } - if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { - yMinI = tt; - } - if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { - yMaxI = tt; - } - } - - // invert the matrix - det = mat[0] * mat[3] - mat[1] * mat[2]; - if (splashAbs(det) < 1e-6) { - // this should be caught by the singular matrix check in fillImageMask - return; - } - det = (SplashCoord)1 / det; - mi0 = det * mat[3] * srcWidth; - mi1 = -det * mat[1] * srcHeight; - mi2 = -det * mat[2] * srcWidth; - mi3 = det * mat[0] * srcHeight; - mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; - mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; - - // grab the image - unscaledImage = (Guchar *)gmallocn(srcWidth, srcHeight); - for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth) { - (*src)(srcData, p); - for (x = 0; x < srcWidth; ++x) { - p[x] = (Guchar)(p[x] * 255); - } - } - - // draw it - pipeInit(&pipe, state->fillPattern, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - for (y = yMinI; y < yMaxI; ++y) { - for (x = xMinI; x < xMaxI; ++x) { - ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; - iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; - if (interpolate) { - if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { - x0 = splashFloor(ix - 0.5); - x1 = x0 + 1; - sx = (ix - 0.5) - x0; - y0 = splashFloor(iy - 0.5); - y1 = y0 + 1; - sy = (iy - 0.5) - y0; - if (x0 < 0) { - x0 = 0; - } - if (x1 >= srcWidth) { - x1 = srcWidth - 1; - } - if (y0 < 0) { - y0 = 0; - } - if (y1 >= srcHeight) { - y1 = srcHeight - 1; - } - pix0 = ((SplashCoord)1 - sx) - * (SplashCoord)unscaledImage[y0 * srcWidth + x0] - + sx * (SplashCoord)unscaledImage[y0 * srcWidth + x1]; - pix1 = ((SplashCoord)1 - sx) - * (SplashCoord)unscaledImage[y1 * srcWidth + x0] - + sx * (SplashCoord)unscaledImage[y1 * srcWidth + x1]; - scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 - + sy * pix1); - } else { - scanBuf[x] = 0; - } - } else { - x0 = splashFloor(ix); - y0 = splashFloor(iy); - if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { - scanBuf[x] = unscaledImage[y0 * srcWidth + x0]; - } else { - scanBuf[x] = 0; - } - } - } - if (clipRes != splashClipAllInside) { - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, - state->strokeAdjust); - } - } - (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, NULL); - } - - gfree(unscaledImage); -} - -// The glyphMode flag is not currently used, but may be useful if the -// stroke adjustment behavior is changed. -void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool glyphMode, - GBool interpolate) { - SplashBitmap *scaledMask; - SplashClipResult clipRes; - SplashPipe pipe; - int scaledWidth, scaledHeight, t0, t1; - SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; - SplashCoord vx[4], vy[4]; - int xMin, yMin, xMax, yMax; - ImageSection section[3]; - int nSections; - int bw, y, xa, xb, x, i, xx, yy; - - // compute the four vertices of the target quadrilateral - vx[0] = mat[4]; vy[0] = mat[5]; - vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; - vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; - vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; - - // clipping - xMin = splashRound(vx[0]); - xMax = splashRound(vx[0]); - yMin = splashRound(vy[0]); - yMax = splashRound(vy[0]); - for (i = 1; i < 4; ++i) { - t0 = splashRound(vx[i]); - if (t0 < xMin) { - xMin = t0; - } else if (t0 > xMax) { - xMax = t0; - } - t1 = splashRound(vy[i]); - if (t1 < yMin) { - yMin = t1; - } else if (t1 > yMax) { - yMax = t1; - } - } - clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes == splashClipAllOutside) { - return; - } - - // compute the scale factors - if (mat[0] >= 0) { - t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); - } else { - t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); - } - if (mat[1] >= 0) { - t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); - } else { - t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); - } - scaledWidth = t0 > t1 ? t0 : t1; - if (mat[2] >= 0) { - t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); - } else { - t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); - } - if (mat[3] >= 0) { - t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); - } else { - t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); - } - scaledHeight = t0 > t1 ? t0 : t1; - if (scaledWidth == 0) { - scaledWidth = 1; - } - if (scaledHeight == 0) { - scaledHeight = 1; - } - - // compute the inverse transform (after scaling) matrix - r00 = mat[0] / scaledWidth; - r01 = mat[1] / scaledWidth; - r10 = mat[2] / scaledHeight; - r11 = mat[3] / scaledHeight; - det = r00 * r11 - r01 * r10; - if (splashAbs(det) < 1e-6) { - // this should be caught by the singular matrix check in fillImageMask - return; - } - ir00 = r11 / det; - ir01 = -r01 / det; - ir10 = -r10 / det; - ir11 = r00 / det; - - // scale the input image - scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, interpolate); - - // construct the three sections - i = 0; - if (vy[1] < vy[i]) { - i = 1; - } - if (vy[2] < vy[i]) { - i = 2; - } - if (vy[3] < vy[i]) { - i = 3; - } - // NB: if using fixed point, 0.000001 will be truncated to zero, - // so these two comparisons must be <=, not < - if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && - vy[(i-1) & 3] < vy[(i+1) & 3]) { - i = (i-1) & 3; - } - if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { - section[0].y0 = splashRound(vy[i]); - section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; - if (vx[i] < vx[(i+1) & 3]) { - section[0].ia0 = i; - section[0].ia1 = (i+3) & 3; - section[0].ib0 = (i+1) & 3; - section[0].ib1 = (i+2) & 3; - } else { - section[0].ia0 = (i+1) & 3; - section[0].ia1 = (i+2) & 3; - section[0].ib0 = i; - section[0].ib1 = (i+3) & 3; - } - nSections = 1; - } else { - section[0].y0 = splashRound(vy[i]); - section[2].y1 = splashRound(vy[(i+2) & 3]) - 1; - section[0].ia0 = section[0].ib0 = i; - section[2].ia1 = section[2].ib1 = (i+2) & 3; - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[0].ia1 = section[2].ia0 = (i+1) & 3; - section[0].ib1 = section[2].ib0 = (i+3) & 3; - } else { - section[0].ia1 = section[2].ia0 = (i+3) & 3; - section[0].ib1 = section[2].ib0 = (i+1) & 3; - } - if (vy[(i+1) & 3] < vy[(i+3) & 3]) { - section[1].y0 = splashRound(vy[(i+1) & 3]); - section[2].y0 = splashRound(vy[(i+3) & 3]); - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[1].ia0 = (i+1) & 3; - section[1].ia1 = (i+2) & 3; - section[1].ib0 = i; - section[1].ib1 = (i+3) & 3; - } else { - section[1].ia0 = i; - section[1].ia1 = (i+3) & 3; - section[1].ib0 = (i+1) & 3; - section[1].ib1 = (i+2) & 3; - } - } else { - section[1].y0 = splashRound(vy[(i+3) & 3]); - section[2].y0 = splashRound(vy[(i+1) & 3]); - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[1].ia0 = i; - section[1].ia1 = (i+1) & 3; - section[1].ib0 = (i+3) & 3; - section[1].ib1 = (i+2) & 3; - } else { - section[1].ia0 = (i+3) & 3; - section[1].ia1 = (i+2) & 3; - section[1].ib0 = i; - section[1].ib1 = (i+1) & 3; - } - } - section[0].y1 = section[1].y0 - 1; - section[1].y1 = section[2].y0 - 1; - nSections = 3; - } - for (i = 0; i < nSections; ++i) { - section[i].xa0 = vx[section[i].ia0]; - section[i].ya0 = vy[section[i].ia0]; - section[i].xa1 = vx[section[i].ia1]; - section[i].ya1 = vy[section[i].ia1]; - section[i].xb0 = vx[section[i].ib0]; - section[i].yb0 = vy[section[i].ib0]; - section[i].xb1 = vx[section[i].ib1]; - section[i].yb1 = vy[section[i].ib1]; - section[i].dxdya = (section[i].xa1 - section[i].xa0) / - (section[i].ya1 - section[i].ya0); - section[i].dxdyb = (section[i].xb1 - section[i].xb0) / - (section[i].yb1 - section[i].yb0); - } - - // initialize the pixel pipe - pipeInit(&pipe, state->fillPattern, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - - // make sure narrow images cover at least one pixel - if (nSections == 1) { - if (section[0].y0 == section[0].y1) { - ++section[0].y1; - clipRes = opClipRes = splashClipPartial; - } - } else { - if (section[0].y0 == section[2].y1) { - ++section[1].y1; - clipRes = opClipRes = splashClipPartial; - } - } - - // scan all pixels inside the target region - bw = bitmap->width; - for (i = 0; i < nSections; ++i) { - for (y = section[i].y0; y <= section[i].y1; ++y) { - xa = splashRound(section[i].xa0 + - ((SplashCoord)y + 0.5 - section[i].ya0) * - section[i].dxdya); - xb = splashRound(section[i].xb0 + - ((SplashCoord)y + 0.5 - section[i].yb0) * - section[i].dxdyb); - if (xa > xb) { - continue; - } - // make sure narrow images cover at least one pixel - if (xa == xb) { - ++xb; - } - // check the scanBuf bounds - if (xa >= bw || xb < 0) { - continue; - } - if (xa < 0) { - xa = 0; - } - if (xb > bw) { - xb = bw; - } - // get the scan line - for (x = xa; x < xb; ++x) { - // map (x+0.5, y+0.5) back to the scaled image - xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + - ((SplashCoord)y + 0.5 - mat[5]) * ir10); - yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + - ((SplashCoord)y + 0.5 - mat[5]) * ir11); - // xx should always be within bounds, but floating point - // inaccuracy can cause problems - if (xx < 0) { - xx = 0; - } else if (xx >= scaledWidth) { - xx = scaledWidth - 1; - } - if (yy < 0) { - yy = 0; - } else if (yy >= scaledHeight) { - yy = scaledHeight - 1; - } - scanBuf[x] = scaledMask->data[yy * scaledWidth + xx]; - } - // clip the scan line - if (clipRes != splashClipAllInside) { - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, - state->strokeAdjust); - } - } - // draw the scan line - (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, NULL); - } - } - - delete scaledMask; -} - -// Scale an image mask into a SplashBitmap. -SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - GBool interpolate) { - SplashBitmap *dest; - - dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, - gFalse); - if (scaledHeight < srcHeight) { - if (scaledWidth < srcWidth) { - scaleMaskYdXd(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); - } else { - scaleMaskYdXu(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); - } - } else { - if (scaledWidth < srcWidth) { - scaleMaskYuXd(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); - } else { - if (interpolate) { - scaleMaskYuXuI(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); - } else { - scaleMaskYuXu(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); - } - } - } - return dest; -} - -void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf; - Guint *pixBuf; - Guint pix; - Guchar *destPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; - int i, j; - - // Bresenham parameters for y scale - yp = srcHeight / scaledHeight; - yq = srcHeight % scaledHeight; - - // Bresenham parameters for x scale - xp = srcWidth / scaledWidth; - xq = srcWidth % scaledWidth; - - // allocate buffers - lineBuf = (Guchar *)gmalloc(srcWidth); - pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - for (y = 0; y < scaledHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= scaledHeight) { - yt -= scaledHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read rows from image - memset(pixBuf, 0, srcWidth * sizeof(int)); - for (i = 0; i < yStep; ++i) { - (*src)(srcData, lineBuf); - for (j = 0; j < srcWidth; ++j) { - pixBuf[j] += lineBuf[j]; - } - } - - // init x scale Bresenham - xt = 0; - d0 = (255 << 23) / (yStep * xp); - d1 = (255 << 23) / (yStep * (xp + 1)); - - xx = 0; - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= scaledWidth) { - xt -= scaledWidth; - xStep = xp + 1; - d = d1; - } else { - xStep = xp; - d = d0; - } - - // compute the final pixel - pix = 0; - for (i = 0; i < xStep; ++i) { - pix += pixBuf[xx++]; - } - // (255 * pix) / xStep * yStep - pix = (pix * d) >> 23; - - // store the pixel - *destPtr++ = (Guchar)pix; - } - } - - gfree(pixBuf); - gfree(lineBuf); -} - -void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf; - Guint *pixBuf; - Guint pix; - Guchar *destPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; - int i, j; - - // Bresenham parameters for y scale - yp = srcHeight / scaledHeight; - yq = srcHeight % scaledHeight; - - // Bresenham parameters for x scale - xp = scaledWidth / srcWidth; - xq = scaledWidth % srcWidth; - - // allocate buffers - lineBuf = (Guchar *)gmalloc(srcWidth); - pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - for (y = 0; y < scaledHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= scaledHeight) { - yt -= scaledHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read rows from image - memset(pixBuf, 0, srcWidth * sizeof(int)); - for (i = 0; i < yStep; ++i) { - (*src)(srcData, lineBuf); - for (j = 0; j < srcWidth; ++j) { - pixBuf[j] += lineBuf[j]; - } - } - - // init x scale Bresenham - xt = 0; - d = (255 << 23) / yStep; - - for (x = 0; x < srcWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= srcWidth) { - xt -= srcWidth; - xStep = xp + 1; - } else { - xStep = xp; - } - - // compute the final pixel - pix = pixBuf[x]; - // (255 * pix) / yStep - pix = (pix * d) >> 23; - - // store the pixel - for (i = 0; i < xStep; ++i) { - *destPtr++ = (Guchar)pix; - } - } - } - - gfree(pixBuf); - gfree(lineBuf); -} - -void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf; - Guint pix; - Guchar *destPtr0, *destPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; - int i; - - // Bresenham parameters for y scale - yp = scaledHeight / srcHeight; - yq = scaledHeight % srcHeight; - - // Bresenham parameters for x scale - xp = srcWidth / scaledWidth; - xq = srcWidth % scaledWidth; - - // allocate buffers - lineBuf = (Guchar *)gmalloc(srcWidth); - - // init y scale Bresenham - yt = 0; - - destPtr0 = dest->data; - for (y = 0; y < srcHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= srcHeight) { - yt -= srcHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read row from image - (*src)(srcData, lineBuf); - - // init x scale Bresenham - xt = 0; - d0 = (255 << 23) / xp; - d1 = (255 << 23) / (xp + 1); - - xx = 0; - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= scaledWidth) { - xt -= scaledWidth; - xStep = xp + 1; - d = d1; - } else { - xStep = xp; - d = d0; - } - - // compute the final pixel - pix = 0; - for (i = 0; i < xStep; ++i) { - pix += lineBuf[xx++]; - } - // (255 * pix) / xStep - pix = (pix * d) >> 23; - - // store the pixel - for (i = 0; i < yStep; ++i) { - destPtr = destPtr0 + i * scaledWidth + x; - *destPtr = (Guchar)pix; - } - } - - destPtr0 += yStep * scaledWidth; - } - - gfree(lineBuf); -} - -void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf; - Guchar pix; - Guchar *srcPtr, *destPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep; - int i; - - // Bresenham parameters for y scale - yp = scaledHeight / srcHeight; - yq = scaledHeight % srcHeight; - - // Bresenham parameters for x scale - xp = scaledWidth / srcWidth; - xq = scaledWidth % srcWidth; - - // allocate buffers - lineBuf = (Guchar *)gmalloc(srcWidth); - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - for (y = 0; y < srcHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= srcHeight) { - yt -= srcHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read row from image - (*src)(srcData, lineBuf); - - // init x scale Bresenham - xt = 0; - - // generate one row - srcPtr = lineBuf; - for (x = 0; x < srcWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= srcWidth) { - xt -= srcWidth; - xStep = xp + 1; - } else { - xStep = xp; - } - - // compute the final pixel - pix = *srcPtr ? 255 : 0; - ++srcPtr; - - // duplicate the pixel horizontally - for (i = 0; i < xStep; ++i) { - *destPtr++ = pix; - } - } - - // duplicate the row vertically - for (i = 1 ; i < yStep; ++i) { - memcpy(destPtr, destPtr - scaledWidth, scaledWidth); - destPtr += scaledWidth; - } - } - - gfree(lineBuf); -} - -void Splash::scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf0, *lineBuf1, *tBuf; - Guchar pix; - SplashCoord yr, xr, ys, xs, ySrc, xSrc; - int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x; - Guchar *destPtr; - - // ratios - yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; - xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; - - // allocate buffers - lineBuf0 = (Guchar *)gmalloc(scaledWidth); - lineBuf1 = (Guchar *)gmalloc(scaledWidth); - - // read first two rows - (*src)(srcData, lineBuf0); - if (srcHeight > 1) { - (*src)(srcData, lineBuf1); - yBuf = 1; - } else { - memcpy(lineBuf1, lineBuf0, srcWidth); - yBuf = 0; - } - - // interpolate first two rows - for (x = scaledWidth - 1; x >= 0; --x) { - xSrc = xr * x; - xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); - xSrc1 = xSrc0 + 1; - xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); - if (xSrc0 < 0) { - xSrc0 = 0; - } - if (xSrc1 >= srcWidth) { - xSrc1 = srcWidth - 1; - } - lineBuf0[x] = (Guchar)(int) - ((xs * (int)lineBuf0[xSrc0] + - ((SplashCoord)1 - xs) * (int)lineBuf0[xSrc1]) * 255); - lineBuf1[x] = (Guchar)(int) - ((xs * (int)lineBuf1[xSrc0] + - ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1]) * 255); - } - - destPtr = dest->data; - for (y = 0; y < scaledHeight; ++y) { - - // compute vertical interpolation parameters - ySrc = yr * y; - ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); - ySrc1 = ySrc0 + 1; - ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); - if (ySrc0 < 0) { - ySrc0 = 0; - ys = 1; - } - if (ySrc1 >= srcHeight) { - ySrc1 = srcHeight - 1; - ys = 0; - } - - // read another row (if necessary) - if (ySrc1 > yBuf) { - tBuf = lineBuf0; - lineBuf0 = lineBuf1; - lineBuf1 = tBuf; - (*src)(srcData, lineBuf1); - - // interpolate the row - for (x = scaledWidth - 1; x >= 0; --x) { - xSrc = xr * x; - xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); - xSrc1 = xSrc0 + 1; - xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); - if (xSrc0 < 0) { - xSrc0 = 0; - } - if (xSrc1 >= srcWidth) { - xSrc1 = srcWidth - 1; - } - lineBuf1[x] = (Guchar)(int) - ((xs * (int)lineBuf1[xSrc0] + - ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1]) * 255); - } - - ++yBuf; - } - - // do the vertical interpolation - for (x = 0; x < scaledWidth; ++x) { - - pix = (Guchar)(int)(ys * (int)lineBuf0[x] + - ((SplashCoord)1 - ys) * (int)lineBuf1[x]); - - // store the pixel - *destPtr++ = pix; - } - } - - gfree(lineBuf1); - gfree(lineBuf0); -} - -void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, - SplashClipResult clipRes) { - SplashPipe pipe; - int w, h, x0, x1, y0, y1, y, t; - - w = src->width; - h = src->height; - pipeInit(&pipe, state->fillPattern, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - if (clipRes == splashClipAllInside) { - for (y = 0; y < h; ++y) { - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - src->data + y * (size_t)w, NULL); - } - } else { - x0 = xDest; - if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { - x0 = t; - } - x1 = xDest + w; - if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { - x1 = t; - } - y0 = yDest; - if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { - y0 = t; - } - y1 = yDest + h; - if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { - y1 = t; - } - if (x0 < x1 && y0 < y1) { - for (y = y0; y < y1; ++y) { - memcpy(scanBuf + x0, - src->data + (y - yDest) * (size_t)w + (x0 - xDest), - x1 - x0); - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, y, x0, x1 - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1, - state->strokeAdjust); - } - (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, NULL); - } - } - } -} - -SplashError Splash::drawImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat, - GBool interpolate) { - GBool ok; - SplashBitmap *scaledImg; - SplashClipResult clipRes; - GBool minorAxisZero; - SplashCoord wSize, hSize, t0, t1; - int x0, y0, x1, y1, scaledWidth, scaledHeight; - int nComps; - - if (debugMode) { - printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", - srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2], - (double)mat[3], (double)mat[4], (double)mat[5]); - } - - // check color modes - ok = gFalse; // make gcc happy - nComps = 0; // make gcc happy - switch (bitmap->mode) { - case splashModeMono1: - case splashModeMono8: - ok = srcMode == splashModeMono8; - nComps = 1; - break; - case splashModeRGB8: - case splashModeBGR8: - ok = srcMode == splashModeRGB8; - nComps = 3; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - ok = srcMode == splashModeCMYK8; - nComps = 4; - break; -#endif - default: - ok = gFalse; - break; - } - if (!ok) { - return splashErrModeMismatch; - } - - // check for singular matrix - if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { - return splashErrSingularMatrix; - } - - minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; - - // rough estimate of size of scaled image - t0 = splashAbs(mat[0]); - t1 = splashAbs(mat[1]); - wSize = t0 > t1 ? t0 : t1; - t0 = splashAbs(mat[2]); - t1 = splashAbs(mat[3]); - hSize = t0 > t1 ? t0 : t1; - - // stream-mode upscaling -- this is slower, so we only use it if the - // upscaled image is large (in which case clipping should remove - // many pixels) -#if USE_FIXEDPOINT - if ((wSize > 2 * w && hSize > 2 * h && (int)wSize > 1000000 / (int)hSize) || - (wSize > w && hSize > h && (int)wSize > 10000000 / (int)hSize) || - ((wSize > w || hSize > h) && (int)wSize > 25000000 / (int)hSize)) { -#else - if ((wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) || - (wSize > w && hSize > h && wSize * hSize > 10000000) || - ((wSize > w || hSize > h) && wSize * hSize > 25000000)) { -#endif - upscaleImage(src, srcData, srcMode, nComps, srcAlpha, - w, h, mat, interpolate); - - // scaling only - } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { - getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); - getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, - scaledWidth, scaledHeight, interpolate); - blitImage(scaledImg, srcAlpha, x0, y0, clipRes); - delete scaledImg; - } - - // scaling plus vertical flip - } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { - getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); - getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, - scaledWidth, scaledHeight, interpolate); - vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); - blitImage(scaledImg, srcAlpha, x0, y0, clipRes); - delete scaledImg; - } - - // scaling plus horizontal flip - } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) { - getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); - getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, - scaledWidth, scaledHeight, interpolate); - horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); - blitImage(scaledImg, srcAlpha, x0, y0, clipRes); - delete scaledImg; - } - - // scaling plus horizontal and vertical flips - } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) { - getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); - getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, - scaledWidth, scaledHeight, interpolate); - vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); - horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); - blitImage(scaledImg, srcAlpha, x0, y0, clipRes); - delete scaledImg; - } - - // all other cases - } else { - arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha, - w, h, mat, interpolate); - } - - return splashOk; -} - -void Splash::upscaleImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - SplashCoord *mat, GBool interpolate) { - SplashClipResult clipRes; - SplashPipe pipe; - SplashColorPtr unscaledImage, pixelBuf, p, q, q00, q01, q10, q11; - Guchar *unscaledAlpha, *alphaPtr; - SplashCoord xMin, yMin, xMax, yMax, t; - SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; - SplashCoord ix, iy, sx, sy, pix0, pix1; - SplashBitmapRowSize rowSize; - int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt, i; - - // compute the bbox of the target quadrilateral - xMin = xMax = mat[4]; - t = mat[2] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - t = mat[0] + mat[2] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - t = mat[0] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - getImageBounds(xMin, xMax, &xMinI, &xMaxI); - yMin = yMax = mat[5]; - t = mat[3] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - t = mat[1] + mat[3] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - t = mat[1] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - getImageBounds(yMin, yMax, &yMinI, &yMaxI); - - // clipping - clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes == splashClipAllOutside) { - return; - } - if (clipRes != splashClipAllInside) { - if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { - xMinI = tt; - } - if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { - xMaxI = tt; - } - if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { - yMinI = tt; - } - if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { - yMaxI = tt; - } - } - - // invert the matrix - det = mat[0] * mat[3] - mat[1] * mat[2]; - if (splashAbs(det) < 1e-6) { - // this should be caught by the singular matrix check in fillImageMask - return; - } - det = (SplashCoord)1 / det; - mi0 = det * mat[3] * srcWidth; - mi1 = -det * mat[1] * srcHeight; - mi2 = -det * mat[2] * srcWidth; - mi3 = det * mat[0] * srcHeight; - mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; - mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; - - // grab the image - if (srcWidth > INT_MAX / nComps) { - rowSize = -1; - } else { - rowSize = srcWidth * nComps; - } - unscaledImage = (SplashColorPtr)gmallocn64(srcHeight, rowSize); - if (srcAlpha) { - unscaledAlpha = (Guchar *)gmallocn(srcHeight, srcWidth); - for (y = 0, p = unscaledImage, alphaPtr = unscaledAlpha; - y < srcHeight; - ++y, p += rowSize, alphaPtr += srcWidth) { - (*src)(srcData, p, alphaPtr); - } - } else { - unscaledAlpha = NULL; - for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += rowSize) { - (*src)(srcData, p, NULL); - } - } - - // draw it - pixelBuf = (SplashColorPtr)gmallocn(xMaxI - xMinI, nComps); - pipeInit(&pipe, NULL, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - for (y = yMinI; y < yMaxI; ++y) { - p = pixelBuf; - for (x = xMinI; x < xMaxI; ++x) { - ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; - iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; - if (interpolate) { - if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { - x0 = splashFloor(ix - 0.5); - x1 = x0 + 1; - sx = (ix - 0.5) - x0; - y0 = splashFloor(iy - 0.5); - y1 = y0 + 1; - sy = (iy - 0.5) - y0; - if (x0 < 0) { - x0 = 0; - } - if (x1 >= srcWidth) { - x1 = srcWidth - 1; - } - if (y0 < 0) { - y0 = 0; - } - if (y1 >= srcHeight) { - y1 = srcHeight - 1; - } - q00 = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x0 * nComps]; - q01 = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x1 * nComps]; - q10 = &unscaledImage[y1 * rowSize + (SplashBitmapRowSize)x0 * nComps]; - q11 = &unscaledImage[y1 * rowSize + (SplashBitmapRowSize)x1 * nComps]; - for (i = 0; i < nComps; ++i) { - pix0 = ((SplashCoord)1 - sx) * (int)*q00++ + sx * (int)*q01++; - pix1 = ((SplashCoord)1 - sx) * (int)*q10++ + sx * (int)*q11++; - *p++ = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 - + sy * pix1); - } - if (srcAlpha) { - pix0 = ((SplashCoord)1 - sx) - * (SplashCoord)unscaledAlpha[y0 * srcWidth + x0] - + sx * (SplashCoord)unscaledAlpha[y0 * srcWidth + x1]; - pix1 = ((SplashCoord)1 - sx) - * (SplashCoord)unscaledAlpha[y1 * srcWidth + x0] - + sx * (SplashCoord)unscaledAlpha[y1 * srcWidth + x1]; - scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 - + sy * pix1); - } else { - scanBuf[x] = 0xff; - } - } else { - for (i = 0; i < nComps; ++i) { - *p++ = 0; - } - scanBuf[x] = 0; - } - } else { - x0 = splashFloor(ix); - y0 = splashFloor(iy); - if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { - q = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x0 * nComps]; - for (i = 0; i < nComps; ++i) { - *p++ = *q++; - } - if (srcAlpha) { - scanBuf[x] = unscaledAlpha[y0 * srcWidth + x0]; - } else { - scanBuf[x] = 0xff; - } - } else { - for (i = 0; i < nComps; ++i) { - *p++ = 0; - } - scanBuf[x] = 0; - } - } - } - if (clipRes != splashClipAllInside) { - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, - state->strokeAdjust); - } - } - (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, pixelBuf); - } - - gfree(pixelBuf); - gfree(unscaledImage); - gfree(unscaledAlpha); -} - -void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool interpolate) { - SplashBitmap *scaledImg; - SplashClipResult clipRes; - SplashPipe pipe; - SplashColorPtr pixelBuf; - int scaledWidth, scaledHeight, t0, t1; - SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; - SplashCoord vx[4], vy[4]; - int xMin, yMin, xMax, yMax; - ImageSection section[3]; - int nSections; - int y, xa, xb, x, i, xx, yy; - - // compute the four vertices of the target quadrilateral - vx[0] = mat[4]; vy[0] = mat[5]; - vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; - vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; - vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; - - // clipping - xMin = splashRound(vx[0]); - xMax = splashRound(vx[0]); - yMin = splashRound(vy[0]); - yMax = splashRound(vy[0]); - for (i = 1; i < 4; ++i) { - t0 = splashRound(vx[i]); - if (t0 < xMin) { - xMin = t0; - } else if (t0 > xMax) { - xMax = t0; - } - t1 = splashRound(vy[i]); - if (t1 < yMin) { - yMin = t1; - } else if (t1 > yMax) { - yMax = t1; - } - } - clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes == splashClipAllOutside) { - return; - } - - // compute the scale factors - if (mat[0] >= 0) { - t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); - } else { - t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); - } - if (mat[1] >= 0) { - t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); - } else { - t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); - } - scaledWidth = t0 > t1 ? t0 : t1; - if (mat[2] >= 0) { - t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); - } else { - t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); - } - if (mat[3] >= 0) { - t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); - } else { - t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); - } - scaledHeight = t0 > t1 ? t0 : t1; - if (scaledWidth == 0) { - scaledWidth = 1; - } - if (scaledHeight == 0) { - scaledHeight = 1; - } - - // compute the inverse transform (after scaling) matrix - r00 = mat[0] / scaledWidth; - r01 = mat[1] / scaledWidth; - r10 = mat[2] / scaledHeight; - r11 = mat[3] / scaledHeight; - det = r00 * r11 - r01 * r10; - if (splashAbs(det) < 1e-6) { - // this should be caught by the singular matrix check in drawImage - return; - } - ir00 = r11 / det; - ir01 = -r01 / det; - ir10 = -r10 / det; - ir11 = r00 / det; - - // scale the input image - scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, - interpolate); - - // construct the three sections - i = 0; - if (vy[1] < vy[i]) { - i = 1; - } - if (vy[2] < vy[i]) { - i = 2; - } - if (vy[3] < vy[i]) { - i = 3; - } - // NB: if using fixed point, 0.000001 will be truncated to zero, - // so these two comparisons must be <=, not < - if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && - vy[(i-1) & 3] < vy[(i+1) & 3]) { - i = (i-1) & 3; - } - if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { - section[0].y0 = splashRound(vy[i]); - section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; - if (vx[i] < vx[(i+1) & 3]) { - section[0].ia0 = i; - section[0].ia1 = (i+3) & 3; - section[0].ib0 = (i+1) & 3; - section[0].ib1 = (i+2) & 3; - } else { - section[0].ia0 = (i+1) & 3; - section[0].ia1 = (i+2) & 3; - section[0].ib0 = i; - section[0].ib1 = (i+3) & 3; - } - nSections = 1; - } else { - section[0].y0 = splashRound(vy[i]); - section[2].y1 = splashRound(vy[(i+2) & 3]) - 1; - section[0].ia0 = section[0].ib0 = i; - section[2].ia1 = section[2].ib1 = (i+2) & 3; - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[0].ia1 = section[2].ia0 = (i+1) & 3; - section[0].ib1 = section[2].ib0 = (i+3) & 3; - } else { - section[0].ia1 = section[2].ia0 = (i+3) & 3; - section[0].ib1 = section[2].ib0 = (i+1) & 3; - } - if (vy[(i+1) & 3] < vy[(i+3) & 3]) { - section[1].y0 = splashRound(vy[(i+1) & 3]); - section[2].y0 = splashRound(vy[(i+3) & 3]); - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[1].ia0 = (i+1) & 3; - section[1].ia1 = (i+2) & 3; - section[1].ib0 = i; - section[1].ib1 = (i+3) & 3; - } else { - section[1].ia0 = i; - section[1].ia1 = (i+3) & 3; - section[1].ib0 = (i+1) & 3; - section[1].ib1 = (i+2) & 3; - } - } else { - section[1].y0 = splashRound(vy[(i+3) & 3]); - section[2].y0 = splashRound(vy[(i+1) & 3]); - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[1].ia0 = i; - section[1].ia1 = (i+1) & 3; - section[1].ib0 = (i+3) & 3; - section[1].ib1 = (i+2) & 3; - } else { - section[1].ia0 = (i+3) & 3; - section[1].ia1 = (i+2) & 3; - section[1].ib0 = i; - section[1].ib1 = (i+1) & 3; - } - } - section[0].y1 = section[1].y0 - 1; - section[1].y1 = section[2].y0 - 1; - nSections = 3; - } - for (i = 0; i < nSections; ++i) { - section[i].xa0 = vx[section[i].ia0]; - section[i].ya0 = vy[section[i].ia0]; - section[i].xa1 = vx[section[i].ia1]; - section[i].ya1 = vy[section[i].ia1]; - section[i].xb0 = vx[section[i].ib0]; - section[i].yb0 = vy[section[i].ib0]; - section[i].xb1 = vx[section[i].ib1]; - section[i].yb1 = vy[section[i].ib1]; - section[i].dxdya = (section[i].xa1 - section[i].xa0) / - (section[i].ya1 - section[i].ya0); - section[i].dxdyb = (section[i].xb1 - section[i].xb0) / - (section[i].yb1 - section[i].yb0); - } - - // initialize the pixel pipe - pipeInit(&pipe, NULL, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - - // make sure narrow images cover at least one pixel - if (nSections == 1) { - if (section[0].y0 == section[0].y1) { - ++section[0].y1; - clipRes = opClipRes = splashClipPartial; - } - } else { - if (section[0].y0 == section[2].y1) { - ++section[1].y1; - clipRes = opClipRes = splashClipPartial; - } - } - - pixelBuf = (SplashColorPtr)gmallocn(xMax - xMin + 1, bitmapComps); - - // scan all pixels inside the target region - for (i = 0; i < nSections; ++i) { - for (y = section[i].y0; y <= section[i].y1; ++y) { - xa = splashRound(section[i].xa0 + - ((SplashCoord)y + 0.5 - section[i].ya0) * - section[i].dxdya); - xb = splashRound(section[i].xb0 + - ((SplashCoord)y + 0.5 - section[i].yb0) * - section[i].dxdyb); - if (xa > xb) { - continue; - } - // make sure narrow images cover at least one pixel - if (xa == xb) { - ++xb; - } - // check the scanBuf bounds - if (xa >= bitmap->width || xb < 0) { - continue; - } - if (xa < 0) { - xa = 0; - } - if (xb > bitmap->width) { - xb = bitmap->width; - } - // clip the scan line - memset(scanBuf + xa, 0xff, xb - xa); - if (clipRes != splashClipAllInside) { - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, y, xa, xb - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, - state->strokeAdjust); - } - } - // draw the scan line - for (x = xa; x < xb; ++x) { - // map (x+0.5, y+0.5) back to the scaled image - xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + - ((SplashCoord)y + 0.5 - mat[5]) * ir10); - yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + - ((SplashCoord)y + 0.5 - mat[5]) * ir11); - // xx should always be within bounds, but floating point - // inaccuracy can cause problems - if (xx < 0) { - xx = 0; - } else if (xx >= scaledWidth) { - xx = scaledWidth - 1; - } - if (yy < 0) { - yy = 0; - } else if (yy >= scaledHeight) { - yy = scaledHeight - 1; - } - // get the color - scaledImg->getPixel(xx, yy, pixelBuf + (x - xa) * bitmapComps); - // apply alpha - if (srcAlpha) { - scanBuf[x] = div255(scanBuf[x] * - scaledImg->alpha[yy * scaledWidth + xx]); - } - } - (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, pixelBuf); - } - } - - gfree(pixelBuf); - delete scaledImg; -} - -// Scale an image into a SplashBitmap. -SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - GBool interpolate) { - SplashBitmap *dest; - - dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha); - if (scaledHeight < srcHeight) { - if (scaledWidth < srcWidth) { - scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); - } else { - scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); - } - } else { - if (scaledWidth < srcWidth) { - scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); - } else { - if (interpolate) { - scaleImageYuXuI(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); - } else { - scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); - } - } - } - return dest; -} - -void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf, *alphaLineBuf; - Guint *pixBuf, *alphaPixBuf; - Guint pix0, pix1, pix2; -#if SPLASH_CMYK - Guint pix3; -#endif - Guint alpha; - Guchar *destPtr, *destAlphaPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; - int i, j; - - // Bresenham parameters for y scale - yp = srcHeight / scaledHeight; - yq = srcHeight % scaledHeight; - - // Bresenham parameters for x scale - xp = srcWidth / scaledWidth; - xq = srcWidth % scaledWidth; - - // allocate buffers - lineBuf = (Guchar *)gmallocn(srcWidth, nComps); - pixBuf = (Guint *)gmallocn(srcWidth, (int)(nComps * sizeof(int))); - if (srcAlpha) { - alphaLineBuf = (Guchar *)gmalloc(srcWidth); - alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); - } else { - alphaLineBuf = NULL; - alphaPixBuf = NULL; - } - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - destAlphaPtr = dest->alpha; - for (y = 0; y < scaledHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= scaledHeight) { - yt -= scaledHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read rows from image - memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); - if (srcAlpha) { - memset(alphaPixBuf, 0, srcWidth * sizeof(int)); - } - for (i = 0; i < yStep; ++i) { - (*src)(srcData, lineBuf, alphaLineBuf); - for (j = 0; j < srcWidth * nComps; ++j) { - pixBuf[j] += lineBuf[j]; - } - if (srcAlpha) { - for (j = 0; j < srcWidth; ++j) { - alphaPixBuf[j] += alphaLineBuf[j]; - } - } - } - - // init x scale Bresenham - xt = 0; - d0 = (1 << 23) / (yStep * xp); - d1 = (1 << 23) / (yStep * (xp + 1)); - - xx = xxa = 0; - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= scaledWidth) { - xt -= scaledWidth; - xStep = xp + 1; - d = d1; - } else { - xStep = xp; - d = d0; - } - - switch (srcMode) { - - case splashModeMono8: - - // compute the final pixel - pix0 = 0; - for (i = 0; i < xStep; ++i) { - pix0 += pixBuf[xx++]; - } - // pix / xStep * yStep - pix0 = (pix0 * d) >> 23; - - // store the pixel - *destPtr++ = (Guchar)pix0; - break; - - case splashModeRGB8: - - // compute the final pixel - pix0 = pix1 = pix2 = 0; - for (i = 0; i < xStep; ++i) { - pix0 += pixBuf[xx]; - pix1 += pixBuf[xx+1]; - pix2 += pixBuf[xx+2]; - xx += 3; - } - // pix / xStep * yStep - pix0 = (pix0 * d) >> 23; - pix1 = (pix1 * d) >> 23; - pix2 = (pix2 * d) >> 23; - - // store the pixel - *destPtr++ = (Guchar)pix0; - *destPtr++ = (Guchar)pix1; - *destPtr++ = (Guchar)pix2; - break; - -#if SPLASH_CMYK - case splashModeCMYK8: - - // compute the final pixel - pix0 = pix1 = pix2 = pix3 = 0; - for (i = 0; i < xStep; ++i) { - pix0 += pixBuf[xx]; - pix1 += pixBuf[xx+1]; - pix2 += pixBuf[xx+2]; - pix3 += pixBuf[xx+3]; - xx += 4; - } - // pix / xStep * yStep - pix0 = (pix0 * d) >> 23; - pix1 = (pix1 * d) >> 23; - pix2 = (pix2 * d) >> 23; - pix3 = (pix3 * d) >> 23; - - // store the pixel - *destPtr++ = (Guchar)pix0; - *destPtr++ = (Guchar)pix1; - *destPtr++ = (Guchar)pix2; - *destPtr++ = (Guchar)pix3; - break; -#endif - - - case splashModeMono1: // mono1 is not allowed - case splashModeBGR8: // bgr8 is not allowed - default: - break; - } - - // process alpha - if (srcAlpha) { - alpha = 0; - for (i = 0; i < xStep; ++i, ++xxa) { - alpha += alphaPixBuf[xxa]; - } - // alpha / xStep * yStep - alpha = (alpha * d) >> 23; - *destAlphaPtr++ = (Guchar)alpha; - } - } - } - - gfree(alphaPixBuf); - gfree(alphaLineBuf); - gfree(pixBuf); - gfree(lineBuf); -} - -void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf, *alphaLineBuf; - Guint *pixBuf, *alphaPixBuf; - Guint pix[splashMaxColorComps]; - Guint alpha; - Guchar *destPtr, *destAlphaPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; - int i, j; - - // Bresenham parameters for y scale - yp = srcHeight / scaledHeight; - yq = srcHeight % scaledHeight; - - // Bresenham parameters for x scale - xp = scaledWidth / srcWidth; - xq = scaledWidth % srcWidth; - - // allocate buffers - lineBuf = (Guchar *)gmallocn(srcWidth, nComps); - pixBuf = (Guint *)gmallocn(srcWidth, (int)(nComps * sizeof(int))); - if (srcAlpha) { - alphaLineBuf = (Guchar *)gmalloc(srcWidth); - alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); - } else { - alphaLineBuf = NULL; - alphaPixBuf = NULL; - } - - // make gcc happy - pix[0] = pix[1] = pix[2] = 0; -#if SPLASH_CMYK - pix[3] = 0; -#endif - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - destAlphaPtr = dest->alpha; - for (y = 0; y < scaledHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= scaledHeight) { - yt -= scaledHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read rows from image - memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); - if (srcAlpha) { - memset(alphaPixBuf, 0, srcWidth * sizeof(int)); - } - for (i = 0; i < yStep; ++i) { - (*src)(srcData, lineBuf, alphaLineBuf); - for (j = 0; j < srcWidth * nComps; ++j) { - pixBuf[j] += lineBuf[j]; - } - if (srcAlpha) { - for (j = 0; j < srcWidth; ++j) { - alphaPixBuf[j] += alphaLineBuf[j]; - } - } - } - - // init x scale Bresenham - xt = 0; - d = (1 << 23) / yStep; - - for (x = 0; x < srcWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= srcWidth) { - xt -= srcWidth; - xStep = xp + 1; - } else { - xStep = xp; - } - - // compute the final pixel - for (i = 0; i < nComps; ++i) { - // pixBuf[] / yStep - pix[i] = (pixBuf[x * nComps + i] * d) >> 23; - } - - // store the pixel - switch (srcMode) { - case splashModeMono8: - for (i = 0; i < xStep; ++i) { - *destPtr++ = (Guchar)pix[0]; - } - break; - case splashModeRGB8: - for (i = 0; i < xStep; ++i) { - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[2]; - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - for (i = 0; i < xStep; ++i) { - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[2]; - *destPtr++ = (Guchar)pix[3]; - } - break; -#endif - case splashModeMono1: // mono1 is not allowed - case splashModeBGR8: // BGR8 is not allowed - default: - break; - } - - // process alpha - if (srcAlpha) { - // alphaPixBuf[] / yStep - alpha = (alphaPixBuf[x] * d) >> 23; - for (i = 0; i < xStep; ++i) { - *destAlphaPtr++ = (Guchar)alpha; - } - } - } - } - - gfree(alphaPixBuf); - gfree(alphaLineBuf); - gfree(pixBuf); - gfree(lineBuf); -} - -void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf, *alphaLineBuf; - Guint pix[splashMaxColorComps]; - Guint alpha; - Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; - int i, j; - - // Bresenham parameters for y scale - yp = scaledHeight / srcHeight; - yq = scaledHeight % srcHeight; - - // Bresenham parameters for x scale - xp = srcWidth / scaledWidth; - xq = srcWidth % scaledWidth; - - // allocate buffers - lineBuf = (Guchar *)gmallocn(srcWidth, nComps); - if (srcAlpha) { - alphaLineBuf = (Guchar *)gmalloc(srcWidth); - } else { - alphaLineBuf = NULL; - } - - // make gcc happy - pix[0] = pix[1] = pix[2] = 0; -#if SPLASH_CMYK - pix[3] = 0; -#endif - - // init y scale Bresenham - yt = 0; - - destPtr0 = dest->data; - destAlphaPtr0 = dest->alpha; - for (y = 0; y < srcHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= srcHeight) { - yt -= srcHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read row from image - (*src)(srcData, lineBuf, alphaLineBuf); - - // init x scale Bresenham - xt = 0; - d0 = (1 << 23) / xp; - d1 = (1 << 23) / (xp + 1); - - xx = xxa = 0; - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= scaledWidth) { - xt -= scaledWidth; - xStep = xp + 1; - d = d1; - } else { - xStep = xp; - d = d0; - } - - // compute the final pixel - for (i = 0; i < nComps; ++i) { - pix[i] = 0; - } - for (i = 0; i < xStep; ++i) { - for (j = 0; j < nComps; ++j, ++xx) { - pix[j] += lineBuf[xx]; - } - } - for (i = 0; i < nComps; ++i) { - // pix[] / xStep - pix[i] = (pix[i] * d) >> 23; - } - - // store the pixel - switch (srcMode) { - case splashModeMono8: - for (i = 0; i < yStep; ++i) { - destPtr = destPtr0 + (i * scaledWidth + x) * nComps; - *destPtr++ = (Guchar)pix[0]; - } - break; - case splashModeRGB8: - for (i = 0; i < yStep; ++i) { - destPtr = destPtr0 + (i * scaledWidth + x) * nComps; - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[2]; - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - for (i = 0; i < yStep; ++i) { - destPtr = destPtr0 + (i * scaledWidth + x) * nComps; - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[2]; - *destPtr++ = (Guchar)pix[3]; - } - break; -#endif - case splashModeMono1: // mono1 is not allowed - case splashModeBGR8: // BGR8 is not allowed - default: - break; - } - - // process alpha - if (srcAlpha) { - alpha = 0; - for (i = 0; i < xStep; ++i, ++xxa) { - alpha += alphaLineBuf[xxa]; - } - // alpha / xStep - alpha = (alpha * d) >> 23; - for (i = 0; i < yStep; ++i) { - destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; - *destAlphaPtr = (Guchar)alpha; - } - } - } - - destPtr0 += yStep * scaledWidth * nComps; - if (srcAlpha) { - destAlphaPtr0 += yStep * scaledWidth; - } - } - - gfree(alphaLineBuf); - gfree(lineBuf); -} - -void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf, *alphaLineBuf; - Guchar pix0, pix1, pix2; -#if SPLASH_CMYK - Guchar pix3; -#endif - Guchar alpha; - Guchar *srcPtr, *srcAlphaPtr; - Guchar *destPtr, *destAlphaPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep; - int i; - - // Bresenham parameters for y scale - yp = scaledHeight / srcHeight; - yq = scaledHeight % srcHeight; - - // Bresenham parameters for x scale - xp = scaledWidth / srcWidth; - xq = scaledWidth % srcWidth; - - // allocate buffers - lineBuf = (Guchar *)gmallocn(srcWidth, nComps); - if (srcAlpha) { - alphaLineBuf = (Guchar *)gmalloc(srcWidth); - } else { - alphaLineBuf = NULL; - } - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - destAlphaPtr = dest->alpha; - for (y = 0; y < srcHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= srcHeight) { - yt -= srcHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read row from image - (*src)(srcData, lineBuf, alphaLineBuf); - - // init x scale Bresenham - xt = 0; - - // generate one row - srcPtr = lineBuf; - srcAlphaPtr = alphaLineBuf; - for (x = 0; x < srcWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= srcWidth) { - xt -= srcWidth; - xStep = xp + 1; - } else { - xStep = xp; - } - - // duplicate the pixel horizontally - switch (srcMode) { - case splashModeMono8: - pix0 = *srcPtr++; - for (i = 0; i < xStep; ++i) { - *destPtr++ = pix0; - } - break; - case splashModeRGB8: - pix0 = *srcPtr++; - pix1 = *srcPtr++; - pix2 = *srcPtr++; - for (i = 0; i < xStep; ++i) { - *destPtr++ = pix0; - *destPtr++ = pix1; - *destPtr++ = pix2; - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - pix0 = *srcPtr++; - pix1 = *srcPtr++; - pix2 = *srcPtr++; - pix3 = *srcPtr++; - for (i = 0; i < xStep; ++i) { - *destPtr++ = pix0; - *destPtr++ = pix1; - *destPtr++ = pix2; - *destPtr++ = pix3; - } - break; -#endif - case splashModeMono1: // mono1 is not allowed - case splashModeBGR8: // BGR8 is not allowed - default: - break; - } - - // duplicate the alpha value horizontally - if (srcAlpha) { - alpha = *srcAlphaPtr++; - for (i = 0; i < xStep; ++i) { - *destAlphaPtr++ = alpha; - } - } - } - - // duplicate the row vertically - for (i = 1; i < yStep; ++i) { - memcpy(destPtr, destPtr - scaledWidth * nComps, - scaledWidth * nComps); - destPtr += scaledWidth * nComps; - } - if (srcAlpha) { - for (i = 1; i < yStep; ++i) { - memcpy(destAlphaPtr, destAlphaPtr - scaledWidth, scaledWidth); - destAlphaPtr += scaledWidth; - } - } - } - - gfree(alphaLineBuf); - gfree(lineBuf); -} - -void Splash::scaleImageYuXuI(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf0, *lineBuf1, *alphaLineBuf0, *alphaLineBuf1, *tBuf; - Guchar pix[splashMaxColorComps]; - SplashCoord yr, xr, ys, xs, ySrc, xSrc; - int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x, i; - Guchar *destPtr, *destAlphaPtr; - - // ratios - yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; - xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; - - // allocate buffers - lineBuf0 = (Guchar *)gmallocn(scaledWidth, nComps); - lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps); - if (srcAlpha) { - alphaLineBuf0 = (Guchar *)gmalloc(scaledWidth); - alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth); - } else { - alphaLineBuf0 = NULL; - alphaLineBuf1 = NULL; - } - - // read first two rows - (*src)(srcData, lineBuf0, alphaLineBuf0); - if (srcHeight > 1) { - (*src)(srcData, lineBuf1, alphaLineBuf1); - yBuf = 1; - } else { - memcpy(lineBuf1, lineBuf0, srcWidth * nComps); - if (srcAlpha) { - memcpy(alphaLineBuf1, alphaLineBuf0, srcWidth); - } - yBuf = 0; - } - - // interpolate first two rows - for (x = scaledWidth - 1; x >= 0; --x) { - xSrc = xr * x; - xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); - xSrc1 = xSrc0 + 1; - xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); - if (xSrc0 < 0) { - xSrc0 = 0; - } - if (xSrc1 >= srcWidth) { - xSrc1 = srcWidth - 1; - } - for (i = 0; i < nComps; ++i) { - lineBuf0[x*nComps+i] = (Guchar)(int) - (xs * (int)lineBuf0[xSrc0*nComps+i] + - ((SplashCoord)1 - xs) * (int)lineBuf0[xSrc1*nComps+i]); - lineBuf1[x*nComps+i] = (Guchar)(int) - (xs * (int)lineBuf1[xSrc0*nComps+i] + - ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1*nComps+i]); - } - if (srcAlpha) { - alphaLineBuf0[x] = (Guchar)(int) - (xs * (int)alphaLineBuf0[xSrc0] + - ((SplashCoord)1 - xs) * (int)alphaLineBuf0[xSrc1]); - alphaLineBuf1[x] = (Guchar)(int) - (xs * (int)alphaLineBuf1[xSrc0] + - ((SplashCoord)1 - xs) * (int)alphaLineBuf1[xSrc1]); - } - } - - // make gcc happy - pix[0] = pix[1] = pix[2] = 0; -#if SPLASH_CMYK - pix[3] = 0; -#endif - - destPtr = dest->data; - destAlphaPtr = dest->alpha; - for (y = 0; y < scaledHeight; ++y) { - - // compute vertical interpolation parameters - ySrc = yr * y; - ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); - ySrc1 = ySrc0 + 1; - ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); - if (ySrc0 < 0) { - ySrc0 = 0; - ys = 1; - } - if (ySrc1 >= srcHeight) { - ySrc1 = srcHeight - 1; - ys = 0; - } - - // read another row (if necessary) - if (ySrc1 > yBuf) { - tBuf = lineBuf0; - lineBuf0 = lineBuf1; - lineBuf1 = tBuf; - tBuf = alphaLineBuf0; - alphaLineBuf0 = alphaLineBuf1; - alphaLineBuf1 = tBuf; - (*src)(srcData, lineBuf1, alphaLineBuf1); - - // interpolate the row - for (x = scaledWidth - 1; x >= 0; --x) { - xSrc = xr * x; - xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); - xSrc1 = xSrc0 + 1; - xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); - if (xSrc0 < 0) { - xSrc0 = 0; - } - if (xSrc1 >= srcWidth) { - xSrc1 = srcWidth - 1; - } - for (i = 0; i < nComps; ++i) { - lineBuf1[x*nComps+i] = (Guchar)(int) - (xs * (int)lineBuf1[xSrc0*nComps+i] + - ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1*nComps+i]); - } - if (srcAlpha) { - alphaLineBuf1[x] = (Guchar)(int) - (xs * (int)alphaLineBuf1[xSrc0] + - ((SplashCoord)1 - xs) * (int)alphaLineBuf1[xSrc1]); - } - } - - ++yBuf; - } - - // do the vertical interpolation - for (x = 0; x < scaledWidth; ++x) { - - for (i = 0; i < nComps; ++i) { - pix[i] = (Guchar)(int) - (ys * (int)lineBuf0[x*nComps+i] + - ((SplashCoord)1 - ys) * (int)lineBuf1[x*nComps+i]); - } - - // store the pixel - switch (srcMode) { - case splashModeMono8: - *destPtr++ = pix[0]; - break; - case splashModeRGB8: - *destPtr++ = pix[0]; - *destPtr++ = pix[1]; - *destPtr++ = pix[2]; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - *destPtr++ = pix[0]; - *destPtr++ = pix[1]; - *destPtr++ = pix[2]; - *destPtr++ = pix[3]; - break; -#endif - case splashModeMono1: // mono1 is not allowed - case splashModeBGR8: // BGR8 is not allowed - default: - break; - } - - // process alpha - if (srcAlpha) { - *destAlphaPtr++ = (Guchar)(int) - (ys * (int)alphaLineBuf0[x] + - ((SplashCoord)1 - ys) * (int)alphaLineBuf1[x]); - } - } - } - - gfree(alphaLineBuf1); - gfree(alphaLineBuf0); - gfree(lineBuf1); - gfree(lineBuf0); -} - -void Splash::vertFlipImage(SplashBitmap *img, int width, int height, - int nComps) { - Guchar *lineBuf; - Guchar *p0, *p1; - int w; - - w = width * nComps; - lineBuf = (Guchar *)gmalloc(w); - for (p0 = img->data, p1 = img->data + (height - 1) * (size_t)w; - p0 < p1; - p0 += w, p1 -= w) { - memcpy(lineBuf, p0, w); - memcpy(p0, p1, w); - memcpy(p1, lineBuf, w); - } - if (img->alpha) { - for (p0 = img->alpha, p1 = img->alpha + (height - 1) * (size_t)width; - p0 < p1; - p0 += width, p1 -= width) { - memcpy(lineBuf, p0, width); - memcpy(p0, p1, width); - memcpy(p1, lineBuf, width); - } - } - gfree(lineBuf); -} - -void Splash::horizFlipImage(SplashBitmap *img, int width, int height, - int nComps) { - Guchar *lineBuf; - SplashColorPtr p0, p1, p2; - int w, x, y, i; - - w = width * nComps; - lineBuf = (Guchar *)gmalloc(w); - for (y = 0, p0 = img->data; y < height; ++y, p0 += img->rowSize) { - memcpy(lineBuf, p0, w); - p1 = p0; - p2 = lineBuf + (w - nComps); - for (x = 0; x < width; ++x) { - for (i = 0; i < nComps; ++i) { - p1[i] = p2[i]; - } - p1 += nComps; - p2 -= nComps; - } - } - if (img->alpha) { - for (y = 0, p0 = img->alpha; y < height; ++y, p0 += width) { - memcpy(lineBuf, p0, width); - p1 = p0; - p2 = lineBuf + (width - 1); - for (x = 0; x < width; ++x) { - *p1++ = *p2--; - } - } - } - gfree(lineBuf); -} - -void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, - SplashClipResult clipRes) { - SplashPipe pipe; - int w, h, x0, y0, x1, y1, y; - - // split the image into clipped and unclipped regions - w = src->width; - h = src->height; - if (clipRes == splashClipAllInside) { - x0 = 0; - y0 = 0; - x1 = w; - y1 = h; - } else { - if (state->clip->getNumPaths()) { - x0 = x1 = w; - y0 = y1 = h; - } else { - if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) { - x0 = 0; - } - if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) { - y0 = 0; - } - if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) { - x1 = w; - } - if (x1 < x0) { - x1 = x0; - } - if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) { - y1 = h; - } - if (y1 < y0) { - y1 = y0; - } - } - } - - // draw the unclipped region - if (x0 < w && y0 < h && x0 < x1 && y0 < y1) { - pipeInit(&pipe, NULL, - (Guchar)splashRound(state->fillAlpha * 255), - srcAlpha, gFalse); - if (srcAlpha) { - for (y = y0; y < y1; ++y) { - (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, - src->alpha + y * src->alphaRowSize + x0, - src->data + y * src->rowSize + x0 * bitmapComps); - } - } else { - for (y = y0; y < y1; ++y) { - (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, - NULL, - src->data + y * src->getRowSize() + - x0 * bitmapComps); - } - } - } - - // draw the clipped regions - if (y0 > 0) { - blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0); - } - if (y1 < h) { - blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1); - } - if (x0 > 0 && y0 < y1) { - blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0); - } - if (x1 < w && y0 < y1) { - blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0, - w - x1, y1 - y0); - } -} - -void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha, - int xSrc, int ySrc, int xDest, int yDest, - int w, int h) { - SplashPipe pipe; - int y; - - if (xDest < 0) { - xSrc -= xDest; - w += xDest; - xDest = 0; - } - if (xDest + w > bitmap->width) { - w = bitmap->width - xDest; - } - if (yDest < 0) { - ySrc -= yDest; - h += yDest; - yDest = 0; - } - if (yDest + h > bitmap->height) { - h = bitmap->height - yDest; - } - if (w <= 0 || h <= 0) { - return; - } - - pipeInit(&pipe, NULL, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - if (srcAlpha) { - for (y = 0; y < h; ++y) { - memcpy(scanBuf + xDest, - src->alpha + (ySrc + y) * src->alphaRowSize + xSrc, - w); - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, - state->strokeAdjust); - } - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - scanBuf + xDest, - src->data + (ySrc + y) * src->rowSize + - xSrc * bitmapComps); - } - } else { - for (y = 0; y < h; ++y) { - memset(scanBuf + xDest, 0xff, w); - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, - state->strokeAdjust); - } - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - scanBuf + xDest, - src->data + (ySrc + y) * src->rowSize + - xSrc * bitmapComps); - } - } -} - -SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, - int xDest, int yDest, int w, int h, - GBool noClip, GBool nonIsolated) { - SplashPipe pipe; - Guchar *mono1Ptr, *lineBuf, *linePtr; - Guchar mono1Mask, b; - int x0, x1, x, y0, y1, y, t; - - if (!(src->mode == bitmap->mode || - (src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) || - (src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) { - return splashErrModeMismatch; - } - - pipeInit(&pipe, NULL, - (Guchar)splashRound(state->fillAlpha * 255), - !noClip || src->alpha != NULL, nonIsolated); - if (src->mode == splashModeMono1) { - // in mono1 mode, pipeRun expects the source to be in mono8 - // format, so we need to extract the source color values into - // scanBuf, expanding them from mono1 to mono8 - if (noClip) { - if (src->alpha) { - for (y = 0; y < h; ++y) { - mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3); - mono1Mask = (Guchar)(0x80 >> (xSrc & 7)); - for (x = 0; x < w; ++x) { - scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; - mono1Ptr += mono1Mask & 1; - mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); - } - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - src->alpha + - (ySrc + y) * src->alphaRowSize + xSrc, - scanBuf); - } - } else { - for (y = 0; y < h; ++y) { - mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3); - mono1Mask = (Guchar)(0x80 >> (xSrc & 7)); - for (x = 0; x < w; ++x) { - scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; - mono1Ptr += mono1Mask & 1; - mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); - } - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - NULL, - scanBuf); - } - } - } else { - x0 = xDest; - if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { - x0 = t; - } - x1 = xDest + w; - if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { - x1 = t; - } - y0 = yDest; - if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { - y0 = t; - } - y1 = yDest + h; - if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { - y1 = t; - } - if (x0 < x1 && y0 < y1) { - if (src->alpha) { - for (y = y0; y < y1; ++y) { - mono1Ptr = src->data - + (ySrc + y - yDest) * src->rowSize - + ((xSrc + x0 - xDest) >> 3); - mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7)); - for (x = x0; x < x1; ++x) { - scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; - mono1Ptr += mono1Mask & 1; - mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); - } - memcpy(scanBuf2 + x0, - src->alpha + (ySrc + y - yDest) * src->alphaRowSize + - (xSrc + x0 - xDest), - x1 - x0); - if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1, - state->strokeAdjust)) { - continue; - } - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, x0, x1 - 1, y, - scanBuf2 + x0, - scanBuf + x0); - } - } else { - for (y = y0; y < y1; ++y) { - mono1Ptr = src->data - + (ySrc + y - yDest) * src->rowSize - + ((xSrc + x0 - xDest) >> 3); - mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7)); - for (x = x0; x < x1; ++x) { - scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; - mono1Ptr += mono1Mask & 1; - mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); - } - memset(scanBuf2 + x0, 0xff, x1 - x0); - if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1, - state->strokeAdjust)) { - continue; - } - (this->*pipe.run)(&pipe, x0, x1 - 1, y, - scanBuf2 + x0, - scanBuf + x0); - } - } - } - } - - } else if (src->mode == splashModeBGR8) { - // in BGR8 mode, pipeRun expects the source to be in RGB8 format, - // so we need to swap bytes - lineBuf = (Guchar *)gmallocn(w, 3); - if (noClip) { - if (src->alpha) { - for (y = 0; y < h; ++y) { - memcpy(lineBuf, - src->data + (ySrc + y) * src->rowSize + xSrc * 3, - w * 3); - for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) { - b = linePtr[0]; - linePtr[0] = linePtr[2]; - linePtr[2] = b; - } - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - src->alpha + - (ySrc + y) * src->alphaRowSize + xSrc, - lineBuf); - } - } else { - for (y = 0; y < h; ++y) { - memcpy(lineBuf, - src->data + (ySrc + y) * src->rowSize + xSrc * 3, - w * 3); - for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) { - b = linePtr[0]; - linePtr[0] = linePtr[2]; - linePtr[2] = b; - } - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - NULL, lineBuf); - } - } - } else { - x0 = xDest; - if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { - x0 = t; - } - x1 = xDest + w; - if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { - x1 = t; - } - y0 = yDest; - if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { - y0 = t; - } - y1 = yDest + h; - if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { - y1 = t; - } - if (x0 < x1 && y0 < y1) { - if (src->alpha) { - for (y = y0; y < y1; ++y) { - memcpy(scanBuf + x0, - src->alpha + (ySrc + y - yDest) * src->alphaRowSize + - (xSrc + x0 - xDest), - x1 - x0); - state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); - memcpy(lineBuf, - src->data + - (ySrc + y - yDest) * src->rowSize + - (xSrc + x0 - xDest) * 3, - (x1 - x0) * 3); - for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) { - b = linePtr[0]; - linePtr[0] = linePtr[2]; - linePtr[2] = b; - } - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, x0, x1 - 1, y, - scanBuf + x0, lineBuf); - } - } else { - for (y = y0; y < y1; ++y) { - memset(scanBuf + x0, 0xff, x1 - x0); - state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); - memcpy(lineBuf, - src->data + - (ySrc + y - yDest) * src->rowSize + - (xSrc + x0 - xDest) * 3, - (x1 - x0) * 3); - for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) { - b = linePtr[0]; - linePtr[0] = linePtr[2]; - linePtr[2] = b; - } - (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y, - scanBuf + x0, - src->data + - (ySrc + y - yDest) * src->rowSize + - (xSrc + x0 - xDest) * bitmapComps); - } - } - } - } - gfree(lineBuf); - - } else { // src->mode not mono1 or BGR8 - if (noClip) { - if (src->alpha) { - for (y = 0; y < h; ++y) { - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - src->alpha + - (ySrc + y) * src->alphaRowSize + xSrc, - src->data + (ySrc + y) * src->rowSize + - xSrc * bitmapComps); - } - } else { - for (y = 0; y < h; ++y) { - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - NULL, - src->data + (ySrc + y) * src->rowSize + - xSrc * bitmapComps); - } - } - } else { - x0 = xDest; - if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { - x0 = t; - } - x1 = xDest + w; - if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { - x1 = t; - } - y0 = yDest; - if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { - y0 = t; - } - y1 = yDest + h; - if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { - y1 = t; - } - if (x0 < x1 && y0 < y1) { - if (src->alpha) { - for (y = y0; y < y1; ++y) { - memcpy(scanBuf + x0, - src->alpha + (ySrc + y - yDest) * src->alphaRowSize + - (xSrc + x0 - xDest), - x1 - x0); - state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, x0, x1 - 1, y, - scanBuf + x0, - src->data + - (ySrc + y - yDest) * src->rowSize + - (xSrc + x0 - xDest) * bitmapComps); - } - } else { - for (y = y0; y < y1; ++y) { - memset(scanBuf + x0, 0xff, x1 - x0); - state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); - (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y, - scanBuf + x0, - src->data + - (ySrc + y - yDest) * src->rowSize + - (xSrc + x0 - xDest) * bitmapComps); - } - } - } - } - } - - return splashOk; -} - -void Splash::compositeBackground(SplashColorPtr color) { - SplashColorPtr p; - Guchar *q; - Guchar alpha, alpha1, c, color0, color1, color2, mask; -#if SPLASH_CMYK - Guchar color3; -#endif - int x, y; - - switch (bitmap->mode) { - case splashModeMono1: - color0 = color[0]; - for (y = 0; y < bitmap->height; ++y) { - p = &bitmap->data[y * bitmap->rowSize]; - q = &bitmap->alpha[y * bitmap->alphaRowSize]; - mask = 0x80; - for (x = 0; x < bitmap->width; ++x) { - alpha = *q++; - if (alpha == 0) { - if (color0 & 0x80) { - *p |= mask; - } else { - *p &= (Guchar)~mask; - } - } else if (alpha != 255) { - alpha1 = (Guchar)(255 - alpha); - c = (*p & mask) ? 0xff : 0x00; - c = div255(alpha1 * color0 + alpha * c); - if (c & 0x80) { - *p |= mask; - } else { - *p &= (Guchar)~mask; - } - } - if (!(mask = (Guchar)(mask >> 1))) { - mask = 0x80; - ++p; - } - } - } - break; - case splashModeMono8: - color0 = color[0]; - for (y = 0; y < bitmap->height; ++y) { - p = &bitmap->data[y * bitmap->rowSize]; - q = &bitmap->alpha[y * bitmap->alphaRowSize]; - for (x = 0; x < bitmap->width; ++x) { - alpha = *q++; - if (alpha == 0) { - p[0] = color0; - } else if (alpha != 255) { - alpha1 = (Guchar)(255 - alpha); - p[0] = div255(alpha1 * color0 + alpha * p[0]); - } - ++p; - } - } - break; - case splashModeRGB8: - case splashModeBGR8: - color0 = color[0]; - color1 = color[1]; - color2 = color[2]; - for (y = 0; y < bitmap->height; ++y) { - p = &bitmap->data[y * bitmap->rowSize]; - q = &bitmap->alpha[y * bitmap->alphaRowSize]; - for (x = 0; x < bitmap->width; ++x) { - alpha = *q++; - if (alpha == 0) { - p[0] = color0; - p[1] = color1; - p[2] = color2; - } else if (alpha != 255) { - alpha1 = (Guchar)(255 - alpha); - p[0] = div255(alpha1 * color0 + alpha * p[0]); - p[1] = div255(alpha1 * color1 + alpha * p[1]); - p[2] = div255(alpha1 * color2 + alpha * p[2]); - } - p += 3; - } - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - color0 = color[0]; - color1 = color[1]; - color2 = color[2]; - color3 = color[3]; - for (y = 0; y < bitmap->height; ++y) { - p = &bitmap->data[y * bitmap->rowSize]; - q = &bitmap->alpha[y * bitmap->alphaRowSize]; - for (x = 0; x < bitmap->width; ++x) { - alpha = *q++; - if (alpha == 0) { - p[0] = color0; - p[1] = color1; - p[2] = color2; - p[3] = color3; - } else if (alpha != 255) { - alpha1 = (Guchar)(255 - alpha); - p[0] = div255(alpha1 * color0 + alpha * p[0]); - p[1] = div255(alpha1 * color1 + alpha * p[1]); - p[2] = div255(alpha1 * color2 + alpha * p[2]); - p[3] = div255(alpha1 * color3 + alpha * p[3]); - } - p += 4; - } - } - break; -#endif - } - memset(bitmap->alpha, 255, bitmap->alphaRowSize * bitmap->height); -} - -SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, - int xDest, int yDest, int w, int h) { - SplashColorPtr p, q; - Guchar mask, srcMask; - int x, y; - - if (src->mode != bitmap->mode) { - return splashErrModeMismatch; - } - - switch (bitmap->mode) { - case splashModeMono1: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; - mask = (Guchar)(0x80 >> (xDest & 7)); - q = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)]; - srcMask = (Guchar)(0x80 >> (xSrc & 7)); - for (x = 0; x < w; ++x) { - if (*q & srcMask) { - *p |= mask; - } else { - *p &= (Guchar)~mask; - } - if (!(mask = (Guchar)(mask >> 1))) { - mask = 0x80; - ++p; - } - if (!(srcMask = (Guchar)(srcMask >> 1))) { - srcMask = 0x80; - ++q; - } - } - } - break; - case splashModeMono8: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; - q = &src->data[(ySrc + y) * src->rowSize + xSrc]; - memcpy(p, q, w); - } - break; - case splashModeRGB8: - case splashModeBGR8: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; - q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc]; - memcpy(p, q, 3 * w); - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; - q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; - memcpy(p, q, 4 * w); - } - break; -#endif - } - - if (bitmap->alpha) { - for (y = 0; y < h; ++y) { - q = &bitmap->alpha[(yDest + y) * bitmap->alphaRowSize + xDest]; - memset(q, 0, w); - } - } - - return splashOk; -} - -SplashError Splash::blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc, - int xDest, int yDest, int w, int h) { - SplashColorPtr p, q; - Guchar *alpha0Ptr; - Guchar alpha0, aSrc, mask, srcMask; - int x, y; - - if (bitmap->mode != dest->mode || - !bitmap->alpha || - !dest->alpha || - !groupBackBitmap) { - return splashErrModeMismatch; - } - - switch (bitmap->mode) { - case splashModeMono1: - for (y = 0; y < h; ++y) { - p = &dest->data[(yDest + y) * dest->rowSize + (xDest >> 3)]; - mask = (Guchar)(0x80 >> (xDest & 7)); - q = &bitmap->data[(ySrc + y) * bitmap->rowSize + (xSrc >> 3)]; - srcMask = (Guchar)(0x80 >> (xSrc & 7)); - for (x = 0; x < w; ++x) { - if (*q & srcMask) { - *p |= mask; - } else { - *p &= (Guchar)~mask; - } - if (!(mask = (Guchar)(mask >> 1))) { - mask = 0x80; - ++p; - } - if (!(srcMask = (Guchar)(srcMask >> 1))) { - srcMask = 0x80; - ++q; - } - } - } - break; - case splashModeMono8: - for (y = 0; y < h; ++y) { - p = &dest->data[(yDest + y) * dest->rowSize + xDest]; - q = &bitmap->data[(ySrc + y) * bitmap->rowSize + xSrc]; - memcpy(p, q, w); - } - break; - case splashModeRGB8: - case splashModeBGR8: - for (y = 0; y < h; ++y) { - p = &dest->data[(yDest + y) * dest->rowSize + 3 * xDest]; - q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 3 * xSrc]; - memcpy(p, q, 3 * w); - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - for (y = 0; y < h; ++y) { - p = &dest->data[(yDest + y) * dest->rowSize + 4 * xDest]; - q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 4 * xSrc]; - memcpy(p, q, 4 * w); - } - break; -#endif - } - - for (y = 0; y < h; ++y) { - p = &dest->alpha[(yDest + y) * dest->alphaRowSize + xDest]; - q = &bitmap->alpha[(ySrc + y) * bitmap->alphaRowSize + xSrc]; - alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + ySrc + y) - * groupBackBitmap->alphaRowSize + - (groupBackX + xSrc)]; - for (x = 0; x < w; ++x) { - alpha0 = *alpha0Ptr++; - aSrc = *q++; - *p++ = (Guchar)(alpha0 + aSrc - div255(alpha0 * aSrc)); - } - } - - return splashOk; -} - -SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w, - int lineCap, int lineJoin, - GBool flatten) { - SplashPath *pathIn, *dashPath, *pathOut; - SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; - SplashCoord crossprod, dotprod, miter, m; - SplashCoord angle, angleNext, dAngle, xc, yc; - SplashCoord dxJoin, dyJoin, dJoin, kappa; - SplashCoord cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4; - GBool first, last, closed; - int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1; - int left0, left1, left2, right0, right1, right2, join0, join1, join2; - int leftFirst, rightFirst, firstPt; - - pathOut = new SplashPath(); - - if (path->length == 0) { - return pathOut; - } - - if (flatten) { - pathIn = flattenPath(path, state->matrix, state->flatness); - if (state->lineDashLength > 0) { - dashPath = makeDashedPath(pathIn); - delete pathIn; - pathIn = dashPath; - if (pathIn->length == 0) { - delete pathIn; - return pathOut; - } - } - } else { - pathIn = path; - } - - subpathStart0 = subpathStart1 = 0; // make gcc happy - seg = 0; // make gcc happy - closed = gFalse; // make gcc happy - left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy - leftFirst = rightFirst = firstPt = 0; // make gcc happy - - i0 = 0; - for (i1 = i0; - !(pathIn->flags[i1] & splashPathLast) && - i1 + 1 < pathIn->length && - pathIn->pts[i1+1].x == pathIn->pts[i1].x && - pathIn->pts[i1+1].y == pathIn->pts[i1].y; - ++i1) ; - - while (i1 < pathIn->length) { - if ((first = pathIn->flags[i0] & splashPathFirst)) { - subpathStart0 = i0; - subpathStart1 = i1; - seg = 0; - closed = pathIn->flags[i0] & splashPathClosed; - } - j0 = i1 + 1; - if (j0 < pathIn->length) { - for (j1 = j0; - !(pathIn->flags[j1] & splashPathLast) && - j1 + 1 < pathIn->length && - pathIn->pts[j1+1].x == pathIn->pts[j1].x && - pathIn->pts[j1+1].y == pathIn->pts[j1].y; - ++j1) ; - } else { - j1 = j0; - } - if (pathIn->flags[i1] & splashPathLast) { - if (first && lineCap == splashLineCapRound) { - // special case: zero-length subpath with round line caps --> - // draw a circle - pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, - pathIn->pts[i0].y); - pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, - pathIn->pts[i0].y + bezierCircle2 * w, - pathIn->pts[i0].x + bezierCircle2 * w, - pathIn->pts[i0].y + (SplashCoord)0.5 * w, - pathIn->pts[i0].x, - pathIn->pts[i0].y + (SplashCoord)0.5 * w); - pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w, - pathIn->pts[i0].y + (SplashCoord)0.5 * w, - pathIn->pts[i0].x - (SplashCoord)0.5 * w, - pathIn->pts[i0].y + bezierCircle2 * w, - pathIn->pts[i0].x - (SplashCoord)0.5 * w, - pathIn->pts[i0].y); - pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w, - pathIn->pts[i0].y - bezierCircle2 * w, - pathIn->pts[i0].x - bezierCircle2 * w, - pathIn->pts[i0].y - (SplashCoord)0.5 * w, - pathIn->pts[i0].x, - pathIn->pts[i0].y - (SplashCoord)0.5 * w); - pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w, - pathIn->pts[i0].y - (SplashCoord)0.5 * w, - pathIn->pts[i0].x + (SplashCoord)0.5 * w, - pathIn->pts[i0].y - bezierCircle2 * w, - pathIn->pts[i0].x + (SplashCoord)0.5 * w, - pathIn->pts[i0].y); - pathOut->close(); - } - i0 = j0; - i1 = j1; - continue; - } - last = pathIn->flags[j1] & splashPathLast; - if (last) { - k0 = subpathStart1 + 1; - } else { - k0 = j1 + 1; - } - for (k1 = k0; - !(pathIn->flags[k1] & splashPathLast) && - k1 + 1 < pathIn->length && - pathIn->pts[k1+1].x == pathIn->pts[k1].x && - pathIn->pts[k1+1].y == pathIn->pts[k1].y; - ++k1) ; - - // compute the deltas for segment (i1, j0) -#if USE_FIXEDPOINT - // the 1/d value can be small, which introduces significant - // inaccuracies in fixed point mode - d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, - pathIn->pts[j0].x, pathIn->pts[j0].y); - dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d; - dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d; -#else - d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, - pathIn->pts[j0].x, pathIn->pts[j0].y); - dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x); - dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y); -#endif - wdx = (SplashCoord)0.5 * w * dx; - wdy = (SplashCoord)0.5 * w * dy; - - // draw the start cap - if (i0 == subpathStart0) { - firstPt = pathOut->length; - } - if (first && !closed) { - switch (lineCap) { - case splashLineCapButt: - pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); - pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); - break; - case splashLineCapRound: - pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); - pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx, - pathIn->pts[i0].y + wdx - bezierCircle * wdy, - pathIn->pts[i0].x - wdx - bezierCircle * wdy, - pathIn->pts[i0].y - wdy + bezierCircle * wdx, - pathIn->pts[i0].x - wdx, - pathIn->pts[i0].y - wdy); - pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy, - pathIn->pts[i0].y - wdy - bezierCircle * wdx, - pathIn->pts[i0].x + wdy - bezierCircle * wdx, - pathIn->pts[i0].y - wdx - bezierCircle * wdy, - pathIn->pts[i0].x + wdy, - pathIn->pts[i0].y - wdx); - break; - case splashLineCapProjecting: - pathOut->moveTo(pathIn->pts[i0].x - wdx - wdy, - pathIn->pts[i0].y + wdx - wdy); - pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy, - pathIn->pts[i0].y - wdx - wdy); - break; - } - } else { - pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); - pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); - } - - // draw the left side of the segment rectangle and the end cap - left2 = pathOut->length - 1; - if (last && !closed) { - switch (lineCap) { - case splashLineCapButt: - pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); - pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); - break; - case splashLineCapRound: - pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); - pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx, - pathIn->pts[j0].y - wdx + bezierCircle * wdy, - pathIn->pts[j0].x + wdx + bezierCircle * wdy, - pathIn->pts[j0].y + wdy - bezierCircle * wdx, - pathIn->pts[j0].x + wdx, - pathIn->pts[j0].y + wdy); - pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy, - pathIn->pts[j0].y + wdy + bezierCircle * wdx, - pathIn->pts[j0].x - wdy + bezierCircle * wdx, - pathIn->pts[j0].y + wdx + bezierCircle * wdy, - pathIn->pts[j0].x - wdy, - pathIn->pts[j0].y + wdx); - break; - case splashLineCapProjecting: - pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx, - pathIn->pts[j0].y - wdx + wdy); - pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx, - pathIn->pts[j0].y + wdx + wdy); - break; - } - } else { - pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); - pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); - } - - // draw the right side of the segment rectangle - // (NB: if stroke adjustment is enabled, the closepath operation MUST - // add a segment because this segment is used for a hint) - right2 = pathOut->length - 1; - pathOut->close(state->strokeAdjust != splashStrokeAdjustOff); - - // draw the join - join2 = pathOut->length; - if (!last || closed) { - - // compute the deltas for segment (j1, k0) -#if USE_FIXEDPOINT - // the 1/d value can be small, which introduces significant - // inaccuracies in fixed point mode - d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, - pathIn->pts[k0].x, pathIn->pts[k0].y); - dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d; - dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d; -#else - d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, - pathIn->pts[k0].x, pathIn->pts[k0].y); - dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x); - dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y); -#endif - wdxNext = (SplashCoord)0.5 * w * dxNext; - wdyNext = (SplashCoord)0.5 * w * dyNext; - - // compute the join parameters - crossprod = dx * dyNext - dy * dxNext; - dotprod = -(dx * dxNext + dy * dyNext); - if (dotprod > 0.9999) { - // avoid a divide-by-zero -- set miter to something arbitrary - // such that sqrt(miter) will exceed miterLimit (and m is never - // used in that situation) - // (note: the comparison value (0.9999) has to be less than - // 1-epsilon, where epsilon is the smallest value - // representable in the fixed point format) - miter = (state->miterLimit + 1) * (state->miterLimit + 1); - m = 0; - } else { - miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod); - if (miter < 1) { - // this can happen because of floating point inaccuracies - miter = 1; - } - m = splashSqrt(miter - 1); - } - - // round join - if (lineJoin == splashLineJoinRound) { - // join angle < 180 - if (crossprod < 0) { - angle = atan2((double)dx, (double)-dy); - angleNext = atan2((double)dxNext, (double)-dyNext); - if (angle < angleNext) { - angle += 2 * M_PI; - } - dAngle = (angle - angleNext) / M_PI; - if (dAngle < 0.501) { - // span angle is <= 90 degrees -> draw a single arc - kappa = dAngle * bezierCircle * w; - cx1 = pathIn->pts[j0].x - wdy + kappa * dx; - cy1 = pathIn->pts[j0].y + wdx + kappa * dy; - cx2 = pathIn->pts[j0].x - wdyNext - kappa * dxNext; - cy2 = pathIn->pts[j0].y + wdxNext - kappa * dyNext; - pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); - pathOut->lineTo(pathIn->pts[j0].x - wdyNext, - pathIn->pts[j0].y + wdxNext); - pathOut->curveTo(cx2, cy2, cx1, cy1, - pathIn->pts[j0].x - wdy, - pathIn->pts[j0].y + wdx); - } else { - // span angle is > 90 degrees -> split into two arcs - dJoin = splashDist(-wdy, wdx, -wdyNext, wdxNext); - if (dJoin > 0) { - dxJoin = (-wdyNext + wdy) / dJoin; - dyJoin = (wdxNext - wdx) / dJoin; - xc = pathIn->pts[j0].x - + (SplashCoord)0.5 * w - * cos((double)((SplashCoord)0.5 * (angle + angleNext))); - yc = pathIn->pts[j0].y - + (SplashCoord)0.5 * w - * sin((double)((SplashCoord)0.5 * (angle + angleNext))); - kappa = dAngle * bezierCircle2 * w; - cx1 = pathIn->pts[j0].x - wdy + kappa * dx; - cy1 = pathIn->pts[j0].y + wdx + kappa * dy; - cx2 = xc - kappa * dxJoin; - cy2 = yc - kappa * dyJoin; - cx3 = xc + kappa * dxJoin; - cy3 = yc + kappa * dyJoin; - cx4 = pathIn->pts[j0].x - wdyNext - kappa * dxNext; - cy4 = pathIn->pts[j0].y + wdxNext - kappa * dyNext; - pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); - pathOut->lineTo(pathIn->pts[j0].x - wdyNext, - pathIn->pts[j0].y + wdxNext); - pathOut->curveTo(cx4, cy4, cx3, cy3, xc, yc); - pathOut->curveTo(cx2, cy2, cx1, cy1, - pathIn->pts[j0].x - wdy, - pathIn->pts[j0].y + wdx); - } - } - - // join angle >= 180 - } else { - angle = atan2((double)-dx, (double)dy); - angleNext = atan2((double)-dxNext, (double)dyNext); - if (angleNext < angle) { - angleNext += 2 * M_PI; - } - dAngle = (angleNext - angle) / M_PI; - if (dAngle < 0.501) { - // span angle is <= 90 degrees -> draw a single arc - kappa = dAngle * bezierCircle * w; - cx1 = pathIn->pts[j0].x + wdy + kappa * dx; - cy1 = pathIn->pts[j0].y - wdx + kappa * dy; - cx2 = pathIn->pts[j0].x + wdyNext - kappa * dxNext; - cy2 = pathIn->pts[j0].y - wdxNext - kappa * dyNext; - pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); - pathOut->lineTo(pathIn->pts[j0].x + wdy, - pathIn->pts[j0].y - wdx); - pathOut->curveTo(cx1, cy1, cx2, cy2, - pathIn->pts[j0].x + wdyNext, - pathIn->pts[j0].y - wdxNext); - } else { - // span angle is > 90 degrees -> split into two arcs - dJoin = splashDist(wdy, -wdx, wdyNext, -wdxNext); - if (dJoin > 0) { - dxJoin = (wdyNext - wdy) / dJoin; - dyJoin = (-wdxNext + wdx) / dJoin; - xc = pathIn->pts[j0].x - + (SplashCoord)0.5 * w - * cos((double)((SplashCoord)0.5 * (angle + angleNext))); - yc = pathIn->pts[j0].y - + (SplashCoord)0.5 * w - * sin((double)((SplashCoord)0.5 * (angle + angleNext))); - kappa = dAngle * bezierCircle2 * w; - cx1 = pathIn->pts[j0].x + wdy + kappa * dx; - cy1 = pathIn->pts[j0].y - wdx + kappa * dy; - cx2 = xc - kappa * dxJoin; - cy2 = yc - kappa * dyJoin; - cx3 = xc + kappa * dxJoin; - cy3 = yc + kappa * dyJoin; - cx4 = pathIn->pts[j0].x + wdyNext - kappa * dxNext; - cy4 = pathIn->pts[j0].y - wdxNext - kappa * dyNext; - pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); - pathOut->lineTo(pathIn->pts[j0].x + wdy, - pathIn->pts[j0].y - wdx); - pathOut->curveTo(cx1, cy1, cx2, cy2, xc, yc); - pathOut->curveTo(cx3, cy3, cx4, cy4, - pathIn->pts[j0].x + wdyNext, - pathIn->pts[j0].y - wdxNext); - } - } - } - - } else { - pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); - - // join angle < 180 - if (crossprod < 0) { - pathOut->lineTo(pathIn->pts[j0].x - wdyNext, - pathIn->pts[j0].y + wdxNext); - // miter join inside limit - if (lineJoin == splashLineJoinMiter && - splashSqrt(miter) <= state->miterLimit) { - pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m, - pathIn->pts[j0].y + wdx + wdy * m); - pathOut->lineTo(pathIn->pts[j0].x - wdy, - pathIn->pts[j0].y + wdx); - // bevel join or miter join outside limit - } else { - pathOut->lineTo(pathIn->pts[j0].x - wdy, - pathIn->pts[j0].y + wdx); - } - - // join angle >= 180 - } else { - pathOut->lineTo(pathIn->pts[j0].x + wdy, - pathIn->pts[j0].y - wdx); - // miter join inside limit - if (lineJoin == splashLineJoinMiter && - splashSqrt(miter) <= state->miterLimit) { - pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m, - pathIn->pts[j0].y - wdx + wdy * m); - pathOut->lineTo(pathIn->pts[j0].x + wdyNext, - pathIn->pts[j0].y - wdxNext); - // bevel join or miter join outside limit - } else { - pathOut->lineTo(pathIn->pts[j0].x + wdyNext, - pathIn->pts[j0].y - wdxNext); - } - } - } - - pathOut->close(); - } - - // add stroke adjustment hints - if (state->strokeAdjust != splashStrokeAdjustOff) { - - // subpath with one segment - if (seg == 0 && last) { - switch (lineCap) { - case splashLineCapButt: - pathOut->addStrokeAdjustHint(firstPt, left2 + 1, - firstPt, pathOut->length - 1); - break; - case splashLineCapProjecting: - pathOut->addStrokeAdjustHint(firstPt, left2 + 1, - firstPt, pathOut->length - 1, gTrue); - break; - case splashLineCapRound: - break; - } - pathOut->addStrokeAdjustHint(left2, right2, - firstPt, pathOut->length - 1); - } else { - - // start of subpath - if (seg == 1) { - - // start cap - if (!closed) { - switch (lineCap) { - case splashLineCapButt: - pathOut->addStrokeAdjustHint(firstPt, left1 + 1, - firstPt, firstPt + 1); - pathOut->addStrokeAdjustHint(firstPt, left1 + 1, - right1 + 1, right1 + 1); - break; - case splashLineCapProjecting: - pathOut->addStrokeAdjustHint(firstPt, left1 + 1, - firstPt, firstPt + 1, gTrue); - pathOut->addStrokeAdjustHint(firstPt, left1 + 1, - right1 + 1, right1 + 1, gTrue); - break; - case splashLineCapRound: - break; - } - } - - // first segment - pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2); - pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); - } - - // middle of subpath - if (seg > 1) { - pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); - pathOut->addStrokeAdjustHint(left1, right1, join0, left2); - pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); - } - - // end of subpath - if (last) { - - if (closed) { - // first segment - pathOut->addStrokeAdjustHint(leftFirst, rightFirst, - left2 + 1, right2); - pathOut->addStrokeAdjustHint(leftFirst, rightFirst, - join2, pathOut->length - 1); - - // last segment - pathOut->addStrokeAdjustHint(left2, right2, - left1 + 1, right1); - pathOut->addStrokeAdjustHint(left2, right2, - join1, pathOut->length - 1); - pathOut->addStrokeAdjustHint(left2, right2, - leftFirst - 1, leftFirst); - pathOut->addStrokeAdjustHint(left2, right2, - rightFirst + 1, rightFirst + 1); - - } else { - - // last segment - pathOut->addStrokeAdjustHint(left2, right2, - left1 + 1, right1); - pathOut->addStrokeAdjustHint(left2, right2, - join1, pathOut->length - 1); - - // end cap - switch (lineCap) { - case splashLineCapButt: - pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1, - left2 + 1, left2 + 2); - break; - case splashLineCapProjecting: - pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1, - left2 + 1, left2 + 2, gTrue); - break; - case splashLineCapRound: - break; - } - } - } - } - - left0 = left1; - left1 = left2; - right0 = right1; - right1 = right2; - join0 = join1; - join1 = join2; - if (seg == 0) { - leftFirst = left2; - rightFirst = right2; - } - } - - i0 = j0; - i1 = j1; - ++seg; - } - - if (pathIn != path) { - delete pathIn; - } - - return pathOut; -} - -SplashClipResult Splash::limitRectToClipRect(int *xMin, int *yMin, - int *xMax, int *yMax) { - int t; - - if ((t = state->clip->getXMinI(state->strokeAdjust)) > *xMin) { - *xMin = t; - } - if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < *xMax) { - *xMax = t; - } - if ((t = state->clip->getYMinI(state->strokeAdjust)) > *yMin) { - *yMin = t; - } - if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < *yMax) { - *yMax = t; - } - if (*xMin >= *xMax || *yMin >= *yMax) { - return splashClipAllOutside; - } - return state->clip->testRect(*xMin, *yMin, *xMax - 1, *yMax - 1, - state->strokeAdjust); -} - -void Splash::dumpPath(SplashPath *path) { - int i; - - for (i = 0; i < path->length; ++i) { - printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n", - i, (double)path->pts[i].x, (double)path->pts[i].y, - (path->flags[i] & splashPathFirst) ? " first" : "", - (path->flags[i] & splashPathLast) ? " last" : "", - (path->flags[i] & splashPathClosed) ? " closed" : "", - (path->flags[i] & splashPathCurve) ? " curve" : ""); - } - if (path->hintsLength == 0) { - printf(" no hints\n"); - } else { - for (i = 0; i < path->hintsLength; ++i) { - printf(" hint %3d: ctrl0=%d ctrl1=%d pts=%d..%d\n", - i, path->hints[i].ctrl0, path->hints[i].ctrl1, - path->hints[i].firstPt, path->hints[i].lastPt); - } - } -} - -void Splash::dumpXPath(SplashXPath *path) { - int i; - - for (i = 0; i < path->length; ++i) { - printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f count=%d\n", - i, (double)path->segs[i].x0, (double)path->segs[i].y0, - (double)path->segs[i].x1, (double)path->segs[i].y1, - path->segs[i].count); - } -} - diff --git a/test/bug-hunting/cve/CVE-2019-10020/Splash.h b/test/bug-hunting/cve/CVE-2019-10020/Splash.h deleted file mode 100644 index dc667512119..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10020/Splash.h +++ /dev/null @@ -1,449 +0,0 @@ -//======================================================================== -// -// Splash.h -// -// Copyright 2003-2013 Glyph & Cog, LLC -// -//======================================================================== - -#ifndef SPLASH_H -#define SPLASH_H - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "SplashTypes.h" -#include "SplashClip.h" - -class Splash; -class SplashBitmap; -struct SplashGlyphBitmap; -class SplashState; -class SplashPattern; -class SplashScreen; -class SplashPath; -class SplashXPath; -class SplashFont; -struct SplashPipe; - -//------------------------------------------------------------------------ - -// Retrieves the next line of pixels in an image mask. Normally, -// fills in * and returns true. If the image stream is -// exhausted, returns false. -typedef GBool (*SplashImageMaskSource)(void *data, Guchar *pixel); - -// Retrieves the next line of pixels in an image. Normally, fills in -// * and returns true. If the image stream is exhausted, -// returns false. -typedef GBool (*SplashImageSource)(void *data, SplashColorPtr colorLine, - Guchar *alphaLine); - - -//------------------------------------------------------------------------ - -enum SplashPipeResultColorCtrl { - splashPipeResultColorNoAlphaBlendMono, - splashPipeResultColorNoAlphaBlendRGB, -#if SPLASH_CMYK - splashPipeResultColorNoAlphaBlendCMYK, -#endif - splashPipeResultColorAlphaNoBlendMono, - splashPipeResultColorAlphaNoBlendRGB, -#if SPLASH_CMYK - splashPipeResultColorAlphaNoBlendCMYK, -#endif - splashPipeResultColorAlphaBlendMono, - splashPipeResultColorAlphaBlendRGB -#if SPLASH_CMYK - , - splashPipeResultColorAlphaBlendCMYK -#endif -}; - -//------------------------------------------------------------------------ -// Splash -//------------------------------------------------------------------------ - -class Splash { -public: - - // Create a new rasterizer object. - Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, - SplashScreenParams *screenParams = NULL); - Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, - SplashScreen *screenA); - - ~Splash(); - - //----- state read - - SplashCoord *getMatrix(); - SplashPattern *getStrokePattern(); - SplashPattern *getFillPattern(); - SplashScreen *getScreen(); - SplashBlendFunc getBlendFunc(); - SplashCoord getStrokeAlpha(); - SplashCoord getFillAlpha(); - SplashCoord getLineWidth(); - int getLineCap(); - int getLineJoin(); - SplashCoord getMiterLimit(); - SplashCoord getFlatness(); - SplashCoord *getLineDash(); - int getLineDashLength(); - SplashCoord getLineDashPhase(); - SplashStrokeAdjustMode getStrokeAdjust(); - SplashClip *getClip(); - SplashBitmap *getSoftMask(); - GBool getInNonIsolatedGroup(); - GBool getInKnockoutGroup(); - - //----- state write - - void setMatrix(SplashCoord *matrix); - void setStrokePattern(SplashPattern *strokeColor); - void setFillPattern(SplashPattern *fillColor); - void setScreen(SplashScreen *screen); - void setBlendFunc(SplashBlendFunc func); - void setStrokeAlpha(SplashCoord alpha); - void setFillAlpha(SplashCoord alpha); - void setLineWidth(SplashCoord lineWidth); - void setLineCap(int lineCap); - void setLineJoin(int lineJoin); - void setMiterLimit(SplashCoord miterLimit); - void setFlatness(SplashCoord flatness); - // the array will be copied - void setLineDash(SplashCoord *lineDash, int lineDashLength, - SplashCoord lineDashPhase); - void setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust); - // NB: uses transformed coordinates. - void clipResetToRect(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1); - // NB: uses transformed coordinates. - SplashError clipToRect(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1); - // NB: uses untransformed coordinates. - SplashError clipToPath(SplashPath *path, GBool eo); - void setSoftMask(SplashBitmap *softMask); - void setInTransparencyGroup(SplashBitmap *groupBackBitmapA, - int groupBackXA, int groupBackYA, - GBool nonIsolated, GBool knockout); - void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray); - void setOverprintMask(Guint overprintMask); - void setEnablePathSimplification(GBool en); - - //----- state save/restore - - void saveState(); - SplashError restoreState(); - - //----- drawing operations - - // Fill the bitmap with . This is not subject to clipping. - void clear(SplashColorPtr color, Guchar alpha = 0x00); - - // Stroke a path using the current stroke pattern. - SplashError stroke(SplashPath *path); - - // Fill a path using the current fill pattern. - SplashError fill(SplashPath *path, GBool eo); - - // Draw a character, using the current fill pattern. - SplashError fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font); - - // Draw a glyph, using the current fill pattern. This function does - // not free any data, i.e., it ignores glyph->freeData. - SplashError fillGlyph(SplashCoord x, SplashCoord y, - SplashGlyphBitmap *glyph); - - // Draws an image mask using the fill color. This will read - // lines of pixels from , starting with the top line. "1" - // pixels will be drawn with the current fill color; "0" pixels are - // transparent. The matrix: - // [ mat[0] mat[1] 0 ] - // [ mat[2] mat[3] 0 ] - // [ mat[4] mat[5] 1 ] - // maps a unit square to the desired destination for the image, in - // PostScript style: - // [x' y' 1] = [x y 1] * mat - // Note that the Splash y axis points downward, and the image source - // is assumed to produce pixels in raster order, starting from the - // top line. - SplashError fillImageMask(SplashImageMaskSource src, void *srcData, - int w, int h, SplashCoord *mat, - GBool glyphMode, GBool interpolate); - - // Draw an image. This will read lines of pixels from - // , starting with the top line. These pixels are assumed to - // be in the source mode, . If is true, the - // alpha values returned by are used; otherwise they are - // ignored. The following combinations of source and target modes - // are supported: - // source target - // ------ ------ - // Mono8 Mono1 -- with dithering - // Mono8 Mono8 - // RGB8 RGB8 - // BGR8 RGB8 - // CMYK8 CMYK8 - // The matrix behaves as for fillImageMask. - SplashError drawImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat, - GBool interpolate); - - // Composite a rectangular region from onto this Splash - // object. - SplashError composite(SplashBitmap *src, int xSrc, int ySrc, - int xDest, int yDest, int w, int h, - GBool noClip, GBool nonIsolated); - - // Composite this Splash object onto a background color. The - // background alpha is assumed to be 1. - void compositeBackground(SplashColorPtr color); - - // Copy a rectangular region from onto the bitmap belonging to - // this Splash object. The destination alpha values are all set to - // zero. - SplashError blitTransparent(SplashBitmap *src, int xSrc, int ySrc, - int xDest, int yDest, int w, int h); - - // Copy a rectangular region from the bitmap belonging to this - // Splash object to . The alpha values are corrected for a - // non-isolated group. - SplashError blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc, - int xDest, int yDest, int w, int h); - - //----- misc - - // Construct a path for a stroke, given the path to be stroked and - // the line width . All other stroke parameters are taken from - // the current state. If is true, this function will - // first flatten the path and handle the linedash. - SplashPath *makeStrokePath(SplashPath *path, SplashCoord w, - int lineCap, int lineJoin, - GBool flatten = gTrue); - - // Reduce the size of a rectangle as much as possible by moving any - // edges that are completely outside the clip region. Returns the - // clipping status of the resulting rectangle. - SplashClipResult limitRectToClipRect(int *xMin, int *yMin, - int *xMax, int *yMax); - - // Return the associated bitmap. - SplashBitmap *getBitmap() { - return bitmap; - } - - // Set the minimum line width. - void setMinLineWidth(SplashCoord w) { - minLineWidth = w; - } - - // Get a bounding box which includes all modifications since the - // last call to clearModRegion. - void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax) - { - *xMin = modXMin; *yMin = modYMin; *xMax = modXMax; *yMax = modYMax; - } - - // Clear the modified region bounding box. - void clearModRegion(); - - // Get clipping status for the last drawing operation subject to - // clipping. - SplashClipResult getClipRes() { - return opClipRes; - } - - // Toggle debug mode on or off. - void setDebugMode(GBool debugModeA) { - debugMode = debugModeA; - } - -#if 1 //~tmp: turn off anti-aliasing temporarily - void setInShading(GBool sh) { - inShading = sh; - } -#endif - - -private: - - void pipeInit(SplashPipe *pipe, SplashPattern *pattern, - Guchar aInput, GBool usesShape, - GBool nonIsolatedGroup); - void pipeRun(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#if SPLASH_CMYK - void pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#endif - void pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#if SPLASH_CMYK - void pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#endif - void pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#if SPLASH_CMYK - void pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#endif - void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, - SplashCoord *xo, SplashCoord *yo); - void updateModX(int x); - void updateModY(int y); - void strokeNarrow(SplashPath *path); - void drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip); - void strokeWide(SplashPath *path, SplashCoord w, - int lineCap, int lineJoin); - SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness); - void flattenCurve(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1, - SplashCoord x2, SplashCoord y2, - SplashCoord x3, SplashCoord y3, - SplashCoord *matrix, SplashCoord flatness2, - SplashPath *fPath); - SplashPath *makeDashedPath(SplashPath *xPath); - SplashError fillWithPattern(SplashPath *path, GBool eo, - SplashPattern *pattern, SplashCoord alpha); - SplashPath *tweakFillPath(SplashPath *path); - GBool pathAllOutside(SplashPath *path); - SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph); - void getImageBounds(SplashCoord xyMin, SplashCoord xyMax, - int *xyMinI, int *xyMaxI); - void upscaleMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool glyphMode, - GBool interpolate); - void arbitraryTransformMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool glyphMode, - GBool interpolate); - SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - GBool interpolate); - void scaleMaskYdXd(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleMaskYdXu(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleMaskYuXd(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleMaskYuXu(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void blitMask(SplashBitmap *src, int xDest, int yDest, - SplashClipResult clipRes); - void upscaleImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - SplashCoord *mat, GBool interpolate); - void arbitraryTransformImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool interpolate); - SplashBitmap *scaleImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - GBool interpolate); - void scaleImageYdXd(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleImageYdXu(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleImageYuXd(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleImageYuXu(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleImageYuXuI(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void vertFlipImage(SplashBitmap *img, int width, int height, - int nComps); - void horizFlipImage(SplashBitmap *img, int width, int height, - int nComps); - void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, - SplashClipResult clipRes); - void blitImageClipped(SplashBitmap *src, GBool srcAlpha, - int xSrc, int ySrc, int xDest, int yDest, - int w, int h); - void dumpPath(SplashPath *path); - void dumpXPath(SplashXPath *path); - - - static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[]; - static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[]; - static SplashPipeResultColorCtrl pipeResultColorAlphaBlend[]; - static int pipeNonIsoGroupCorrection[]; - - SplashBitmap *bitmap; - int bitmapComps; - SplashState *state; - Guchar *scanBuf; - Guchar *scanBuf2; - SplashBitmap // for transparency groups, this is the bitmap - *groupBackBitmap; // containing the alpha0/color0 values - int groupBackX, groupBackY; // offset within groupBackBitmap - SplashCoord minLineWidth; - int modXMin, modYMin, modXMax, modYMax; - SplashClipResult opClipRes; - GBool vectorAntialias; - GBool inShading; - GBool debugMode; -}; - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-10020/expected.txt b/test/bug-hunting/cve/CVE-2019-10020/expected.txt deleted file mode 100644 index 619e1df84b8..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10020/expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -Splash.cc:5559:bughuntingDivByZero -Splash.cc:5560:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-10021/Stream.cc b/test/bug-hunting/cve/CVE-2019-10021/Stream.cc deleted file mode 100644 index 4e9a2dc2ff4..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10021/Stream.cc +++ /dev/null @@ -1,5849 +0,0 @@ -//======================================================================== -// -// Stream.cc -// -// Copyright 1996-2003 Glyph & Cog, LLC -// -//======================================================================== - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include -#include -#include -#include -#ifdef _WIN32 -#include -#else -#include -#endif -#include -#include -#include "gmem.h" -#include "gmempp.h" -#include "gfile.h" -#if MULTITHREADED -#include "GMutex.h" -#endif -#include "config.h" -#include "Error.h" -#include "Object.h" -#include "Lexer.h" -#include "GfxState.h" -#include "Stream.h" -#include "JBIG2Stream.h" -#include "JPXStream.h" -#include "Stream-CCITT.h" - -#ifdef __DJGPP__ -static GBool setDJSYSFLAGS = gFalse; -#endif - -#ifdef VMS -#ifdef __GNUC__ -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 -#endif -#endif - -//------------------------------------------------------------------------ -// Stream (base class) -//------------------------------------------------------------------------ - -Stream::Stream() { -} - -Stream::~Stream() { -} - -void Stream::close() { -} - -int Stream::getRawChar() { - error(errInternal, -1, "Called getRawChar() on non-predictor stream"); - return EOF; -} - -int Stream::getBlock(char *buf, int size) { - int n, c; - - n = 0; - while (n < size) { - if ((c = getChar()) == EOF) { - break; - } - buf[n++] = (char)c; - } - return n; -} - -char *Stream::getLine(char *buf, int size) { - int i; - int c; - - if (lookChar() == EOF || size < 0) - return NULL; - for (i = 0; i < size - 1; ++i) { - c = getChar(); - if (c == EOF || c == '\n') - break; - if (c == '\r') { - if ((c = lookChar()) == '\n') - getChar(); - break; - } - buf[i] = (char)c; - } - buf[i] = '\0'; - return buf; -} - -Guint Stream::discardChars(Guint n) { - char buf[4096]; - Guint count, i, j; - - count = 0; - while (count < n) { - if ((i = n - count) > sizeof(buf)) { - i = (Guint)sizeof(buf); - } - j = (Guint)getBlock(buf, (int)i); - count += j; - if (j != i) { - break; - } - } - return count; -} - -GString *Stream::getPSFilter(int psLevel, const char *indent) { - return new GString(); -} - -Stream *Stream::addFilters(Object *dict, int recursion) { - Object obj, obj2; - Object params, params2; - Stream *str; - int i; - - str = this; - dict->dictLookup("Filter", &obj); - if (obj.isNull()) { - obj.free(); - dict->dictLookup("F", &obj); - } - dict->dictLookup("DecodeParms", ¶ms); - if (params.isNull()) { - params.free(); - dict->dictLookup("DP", ¶ms); - } - if (obj.isName()) { - str = makeFilter(obj.getName(), str, ¶ms, recursion); - } else if (obj.isArray()) { - for (i = 0; i < obj.arrayGetLength(); ++i) { - obj.arrayGet(i, &obj2, recursion); - if (params.isArray()) - params.arrayGet(i, ¶ms2, recursion); - else - params2.initNull(); - if (obj2.isName()) { - str = makeFilter(obj2.getName(), str, ¶ms2, recursion); - } else { - error(errSyntaxError, getPos(), "Bad filter name"); - str = new EOFStream(str); - } - obj2.free(); - params2.free(); - } - } else if (!obj.isNull()) { - error(errSyntaxError, getPos(), "Bad 'Filter' attribute in stream"); - } - obj.free(); - params.free(); - - return str; -} - -Stream *Stream::makeFilter(char *name, Stream *str, Object *params, - int recursion) { - int pred; // parameters - int colors; - int bits; - int early; - int encoding; - GBool endOfLine, byteAlign, endOfBlock, black; - int columns, rows; - int colorXform; - Object globals, obj; - - if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) { - str = new ASCIIHexStream(str); - } else if (!strcmp(name, "ASCII85Decode") || !strcmp(name, "A85")) { - str = new ASCII85Stream(str); - } else if (!strcmp(name, "LZWDecode") || !strcmp(name, "LZW")) { - pred = 1; - columns = 1; - colors = 1; - bits = 8; - early = 1; - if (params->isDict()) { - params->dictLookup("Predictor", &obj, recursion); - if (obj.isInt()) - pred = obj.getInt(); - obj.free(); - params->dictLookup("Columns", &obj, recursion); - if (obj.isInt()) - columns = obj.getInt(); - obj.free(); - params->dictLookup("Colors", &obj, recursion); - if (obj.isInt()) - colors = obj.getInt(); - obj.free(); - params->dictLookup("BitsPerComponent", &obj, recursion); - if (obj.isInt()) - bits = obj.getInt(); - obj.free(); - params->dictLookup("EarlyChange", &obj, recursion); - if (obj.isInt()) - early = obj.getInt(); - obj.free(); - } - str = new LZWStream(str, pred, columns, colors, bits, early); - } else if (!strcmp(name, "RunLengthDecode") || !strcmp(name, "RL")) { - str = new RunLengthStream(str); - } else if (!strcmp(name, "CCITTFaxDecode") || !strcmp(name, "CCF")) { - encoding = 0; - endOfLine = gFalse; - byteAlign = gFalse; - columns = 1728; - rows = 0; - endOfBlock = gTrue; - black = gFalse; - if (params->isDict()) { - params->dictLookup("K", &obj, recursion); - if (obj.isInt()) { - encoding = obj.getInt(); - } - obj.free(); - params->dictLookup("EndOfLine", &obj, recursion); - if (obj.isBool()) { - endOfLine = obj.getBool(); - } - obj.free(); - params->dictLookup("EncodedByteAlign", &obj, recursion); - if (obj.isBool()) { - byteAlign = obj.getBool(); - } - obj.free(); - params->dictLookup("Columns", &obj, recursion); - if (obj.isInt()) { - columns = obj.getInt(); - } - obj.free(); - params->dictLookup("Rows", &obj, recursion); - if (obj.isInt()) { - rows = obj.getInt(); - } - obj.free(); - params->dictLookup("EndOfBlock", &obj, recursion); - if (obj.isBool()) { - endOfBlock = obj.getBool(); - } - obj.free(); - params->dictLookup("BlackIs1", &obj, recursion); - if (obj.isBool()) { - black = obj.getBool(); - } - obj.free(); - } - str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign, - columns, rows, endOfBlock, black); - } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) { - colorXform = -1; - if (params->isDict()) { - if (params->dictLookup("ColorTransform", &obj, recursion)->isInt()) { - colorXform = obj.getInt(); - } - obj.free(); - } - str = new DCTStream(str, colorXform); - } else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) { - pred = 1; - columns = 1; - colors = 1; - bits = 8; - if (params->isDict()) { - params->dictLookup("Predictor", &obj, recursion); - if (obj.isInt()) - pred = obj.getInt(); - obj.free(); - params->dictLookup("Columns", &obj, recursion); - if (obj.isInt()) - columns = obj.getInt(); - obj.free(); - params->dictLookup("Colors", &obj, recursion); - if (obj.isInt()) - colors = obj.getInt(); - obj.free(); - params->dictLookup("BitsPerComponent", &obj, recursion); - if (obj.isInt()) - bits = obj.getInt(); - obj.free(); - } - str = new FlateStream(str, pred, columns, colors, bits); - } else if (!strcmp(name, "JBIG2Decode")) { - if (params->isDict()) { - params->dictLookup("JBIG2Globals", &globals, recursion); - } - str = new JBIG2Stream(str, &globals); - globals.free(); - } else if (!strcmp(name, "JPXDecode")) { - str = new JPXStream(str); - } else { - error(errSyntaxError, getPos(), "Unknown filter '{0:s}'", name); - str = new EOFStream(str); - } - return str; -} - -//------------------------------------------------------------------------ -// BaseStream -//------------------------------------------------------------------------ - -BaseStream::BaseStream(Object *dictA) { - dict = *dictA; -} - -BaseStream::~BaseStream() { - dict.free(); -} - -//------------------------------------------------------------------------ -// FilterStream -//------------------------------------------------------------------------ - -FilterStream::FilterStream(Stream *strA) { - str = strA; -} - -FilterStream::~FilterStream() { -} - -void FilterStream::close() { - str->close(); -} - -void FilterStream::setPos(GFileOffset pos, int dir) { - error(errInternal, -1, "Called setPos() on FilterStream"); -} - -//------------------------------------------------------------------------ -// ImageStream -//------------------------------------------------------------------------ - -ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) { - int imgLineSize; - - str = strA; - width = widthA; - nComps = nCompsA; - nBits = nBitsA; - - nVals = width * nComps; - inputLineSize = (nVals * nBits + 7) >> 3; - if (width > INT_MAX / nComps || - nVals > (INT_MAX - 7) / nBits) { - // force a call to gmallocn(-1,...), which will throw an exception - inputLineSize = -1; - } - inputLine = (char *)gmallocn(inputLineSize, sizeof(char)); - if (nBits == 8) { - imgLine = (Guchar *)inputLine; - } else { - if (nBits == 1) { - imgLineSize = (nVals + 7) & ~7; - } else { - imgLineSize = nVals; - } - imgLine = (Guchar *)gmallocn(imgLineSize, sizeof(Guchar)); - } - imgIdx = nVals; -} - -ImageStream::~ImageStream() { - if (imgLine != (Guchar *)inputLine) { - gfree(imgLine); - } - gfree(inputLine); -} - -void ImageStream::reset() { - str->reset(); -} - -void ImageStream::close() { - str->close(); -} - -GBool ImageStream::getPixel(Guchar *pix) { - int i; - - if (imgIdx >= nVals) { - if (!getLine()) { - return gFalse; - } - imgIdx = 0; - } - for (i = 0; i < nComps; ++i) { - pix[i] = imgLine[imgIdx++]; - } - return gTrue; -} - -Guchar *ImageStream::getLine() { - Gulong buf, bitMask; - int bits; - int c; - int i; - char *p; - - if (str->getBlock(inputLine, inputLineSize) != inputLineSize) { - return NULL; - } - if (nBits == 1) { - p = inputLine; - for (i = 0; i < nVals; i += 8) { - c = *p++; - imgLine[i+0] = (Guchar)((c >> 7) & 1); - imgLine[i+1] = (Guchar)((c >> 6) & 1); - imgLine[i+2] = (Guchar)((c >> 5) & 1); - imgLine[i+3] = (Guchar)((c >> 4) & 1); - imgLine[i+4] = (Guchar)((c >> 3) & 1); - imgLine[i+5] = (Guchar)((c >> 2) & 1); - imgLine[i+6] = (Guchar)((c >> 1) & 1); - imgLine[i+7] = (Guchar)(c & 1); - } - } else if (nBits == 8) { - // special case: imgLine == inputLine - } else if (nBits == 16) { - for (i = 0; i < nVals; ++i) { - imgLine[i] = (Guchar)inputLine[2*i]; - } - } else { - bitMask = (1 << nBits) - 1; - buf = 0; - bits = 0; - p = inputLine; - for (i = 0; i < nVals; ++i) { - if (bits < nBits) { - buf = (buf << 8) | (*p++ & 0xff); - bits += 8; - } - imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask); - bits -= nBits; - } - } - return imgLine; -} - -void ImageStream::skipLine() { - str->getBlock(inputLine, inputLineSize); -} - - -//------------------------------------------------------------------------ -// StreamPredictor -//------------------------------------------------------------------------ - -StreamPredictor::StreamPredictor(Stream *strA, int predictorA, - int widthA, int nCompsA, int nBitsA) { - str = strA; - predictor = predictorA; - width = widthA; - nComps = nCompsA; - nBits = nBitsA; - predLine = NULL; - ok = gFalse; - - nVals = width * nComps; - pixBytes = (nComps * nBits + 7) >> 3; - rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes; - if (width <= 0 || nComps <= 0 || nBits <= 0 || - nComps > gfxColorMaxComps || - nBits > 16 || - width >= INT_MAX / nComps || // check for overflow in nVals - nVals >= (INT_MAX - 7) / nBits) { // check for overflow in rowBytes - return; - } - predLine = (Guchar *)gmalloc(rowBytes); - - reset(); - - ok = gTrue; -} - -StreamPredictor::~StreamPredictor() { - gfree(predLine); -} - -void StreamPredictor::reset() { - memset(predLine, 0, rowBytes); - predIdx = rowBytes; -} - -int StreamPredictor::lookChar() { - if (predIdx >= rowBytes) { - if (!getNextLine()) { - return EOF; - } - } - return predLine[predIdx]; -} - -int StreamPredictor::getChar() { - if (predIdx >= rowBytes) { - if (!getNextLine()) { - return EOF; - } - } - return predLine[predIdx++]; -} - -int StreamPredictor::getBlock(char *blk, int size) { - int n, m; - - n = 0; - while (n < size) { - if (predIdx >= rowBytes) { - if (!getNextLine()) { - break; - } - } - m = rowBytes - predIdx; - if (m > size - n) { - m = size - n; - } - memcpy(blk + n, predLine + predIdx, m); - predIdx += m; - n += m; - } - return n; -} - -GBool StreamPredictor::getNextLine() { - int curPred; - Guchar upLeftBuf[gfxColorMaxComps * 2 + 1]; - int left, up, upLeft, p, pa, pb, pc; - int c; - Gulong inBuf, outBuf, bitMask; - int inBits, outBits; - int i, j, k, kk; - - // get PNG optimum predictor number - if (predictor >= 10) { - if ((curPred = str->getRawChar()) == EOF) { - return gFalse; - } - curPred += 10; - } else { - curPred = predictor; - } - - // read the raw line, apply PNG (byte) predictor - memset(upLeftBuf, 0, pixBytes + 1); - for (i = pixBytes; i < rowBytes; ++i) { - for (j = pixBytes; j > 0; --j) { - upLeftBuf[j] = upLeftBuf[j-1]; - } - upLeftBuf[0] = predLine[i]; - if ((c = str->getRawChar()) == EOF) { - if (i > pixBytes) { - // this ought to return false, but some (broken) PDF files - // contain truncated image data, and Adobe apparently reads the - // last partial line - break; - } - return gFalse; - } - switch (curPred) { - case 11: // PNG sub - predLine[i] = (Guchar)(predLine[i - pixBytes] + c); - break; - case 12: // PNG up - predLine[i] = (Guchar)(predLine[i] + c); - break; - case 13: // PNG average - predLine[i] = (Guchar)(((predLine[i - pixBytes] + predLine[i]) >> 1) + c); - break; - case 14: // PNG Paeth - left = predLine[i - pixBytes]; - up = predLine[i]; - upLeft = upLeftBuf[pixBytes]; - p = left + up - upLeft; - if ((pa = p - left) < 0) - pa = -pa; - if ((pb = p - up) < 0) - pb = -pb; - if ((pc = p - upLeft) < 0) - pc = -pc; - if (pa <= pb && pa <= pc) - predLine[i] = (Guchar)(left + c); - else if (pb <= pc) - predLine[i] = (Guchar)(up + c); - else - predLine[i] = (Guchar)(upLeft + c); - break; - case 10: // PNG none - default: // no predictor or TIFF predictor - predLine[i] = (Guchar)c; - break; - } - } - - // apply TIFF (component) predictor - if (predictor == 2) { - if (nBits == 8) { - for (i = pixBytes; i < rowBytes; ++i) { - predLine[i] = (Guchar)(predLine[i] + predLine[i - nComps]); - } - } else if (nBits == 16) { - for (i = pixBytes; i < rowBytes; i += 2) { - c = ((predLine[i] + predLine[i - 2*nComps]) << 8) + - predLine[i + 1] + predLine[i + 1 - 2*nComps]; - predLine[i] = (Guchar)(c >> 8); - predLine[i+1] = (Guchar)(c & 0xff); - } - } else { - memset(upLeftBuf, 0, nComps); - bitMask = (1 << nBits) - 1; - inBuf = outBuf = 0; - inBits = outBits = 0; - j = k = pixBytes; - for (i = 0; i < width; ++i) { - for (kk = 0; kk < nComps; ++kk) { - if (inBits < nBits) { - inBuf = (inBuf << 8) | (predLine[j++] & 0xff); - inBits += 8; - } - upLeftBuf[kk] = (Guchar)((upLeftBuf[kk] + - (inBuf >> (inBits - nBits))) & bitMask); - inBits -= nBits; - outBuf = (outBuf << nBits) | upLeftBuf[kk]; - outBits += nBits; - if (outBits >= 8) { - predLine[k++] = (Guchar)(outBuf >> (outBits - 8)); - outBits -= 8; - } - } - } - if (outBits > 0) { - predLine[k++] = (Guchar)((outBuf << (8 - outBits)) + - (inBuf & ((1 << (8 - outBits)) - 1))); - } - } - } - - // reset to start of line - predIdx = pixBytes; - - return gTrue; -} - -//------------------------------------------------------------------------ -// SharedFile -//------------------------------------------------------------------------ - -class SharedFile { -public: - - SharedFile(FILE *fA); - SharedFile *copy(); - void free(); - int readBlock(char *buf, GFileOffset pos, int size); - GFileOffset getSize(); - -private: - - ~SharedFile(); - - FILE *f; - int refCnt; -#if MULTITHREADED - GMutex mutex; -#endif -}; - -SharedFile::SharedFile(FILE *fA) { - f = fA; - refCnt = 1; -#if MULTITHREADED - gInitMutex(&mutex); -#endif -} - -SharedFile::~SharedFile() { -#if MULTITHREADED - gDestroyMutex(&mutex); -#endif -} - -SharedFile *SharedFile::copy() { -#if MULTITHREADED - gLockMutex(&mutex); -#endif - ++refCnt; -#if MULTITHREADED - gUnlockMutex(&mutex); -#endif - return this; -} - -void SharedFile::free() { - int newCount; - -#if MULTITHREADED - gLockMutex(&mutex); -#endif - newCount = --refCnt; -#if MULTITHREADED - gUnlockMutex(&mutex); -#endif - if (newCount == 0) { - delete this; - } -} - -int SharedFile::readBlock(char *buf, GFileOffset pos, int size) { - int n; - -#if MULTITHREADED - gLockMutex(&mutex); -#endif - gfseek(f, pos, SEEK_SET); - n = (int)fread(buf, 1, size, f); -#if MULTITHREADED - gUnlockMutex(&mutex); -#endif - return n; -} - -GFileOffset SharedFile::getSize() { - GFileOffset size; - -#if MULTITHREADED - gLockMutex(&mutex); -#endif - gfseek(f, 0, SEEK_END); - size = gftell(f); -#if MULTITHREADED - gUnlockMutex(&mutex); -#endif - return size; -} - -//------------------------------------------------------------------------ -// FileStream -//------------------------------------------------------------------------ - -FileStream::FileStream(FILE *fA, GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA): - BaseStream(dictA) { - f = new SharedFile(fA); - start = startA; - limited = limitedA; - length = lengthA; - bufPtr = bufEnd = buf; - bufPos = start; -} - -FileStream::FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA): - BaseStream(dictA) { - f = fA->copy(); - start = startA; - limited = limitedA; - length = lengthA; - bufPtr = bufEnd = buf; - bufPos = start; -} - -FileStream::~FileStream() { - f->free(); -} - -Stream *FileStream::copy() { - Object dictA; - - dict.copy(&dictA); - return new FileStream(f, start, limited, length, &dictA); -} - -Stream *FileStream::makeSubStream(GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA) { - return new FileStream(f, startA, limitedA, lengthA, dictA); -} - -void FileStream::reset() { - bufPtr = bufEnd = buf; - bufPos = start; -} - -int FileStream::getBlock(char *blk, int size) { - int n, m; - - n = 0; - while (n < size) { - if (bufPtr >= bufEnd) { - if (!fillBuf()) { - break; - } - } - m = (int)(bufEnd - bufPtr); - if (m > size - n) { - m = size - n; - } - memcpy(blk + n, bufPtr, m); - bufPtr += m; - n += m; - } - return n; -} - -GBool FileStream::fillBuf() { - int n; - - bufPos += (int)(bufEnd - buf); - bufPtr = bufEnd = buf; - if (limited && bufPos >= start + length) { - return gFalse; - } - if (limited && bufPos + fileStreamBufSize > start + length) { - n = (int)(start + length - bufPos); - } else { - n = fileStreamBufSize; - } - n = f->readBlock(buf, bufPos, n); - bufEnd = buf + n; - if (bufPtr >= bufEnd) { - return gFalse; - } - return gTrue; -} - -void FileStream::setPos(GFileOffset pos, int dir) { - GFileOffset size; - - if (dir >= 0) { - bufPos = pos; - } else { - size = f->getSize(); - if (pos <= size) { - bufPos = size - pos; - } else { - bufPos = 0; - } - } - bufPtr = bufEnd = buf; -} - -void FileStream::moveStart(int delta) { - start += delta; - bufPtr = bufEnd = buf; - bufPos = start; -} - -//------------------------------------------------------------------------ -// MemStream -//------------------------------------------------------------------------ - -MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA): - BaseStream(dictA) { - buf = bufA; - start = startA; - length = lengthA; - bufEnd = buf + start + length; - bufPtr = buf + start; - needFree = gFalse; -} - -MemStream::~MemStream() { - if (needFree) { - gfree(buf); - } -} - -Stream *MemStream::copy() { - Object dictA; - - dict.copy(&dictA); - return new MemStream(buf, start, length, &dictA); -} - -Stream *MemStream::makeSubStream(GFileOffset startA, GBool limited, - GFileOffset lengthA, Object *dictA) { - MemStream *subStr; - Guint newStart, newLength; - - if (startA < start) { - newStart = start; - } else if (startA > start + length) { - newStart = start + (int)length; - } else { - newStart = (int)startA; - } - if (!limited || newStart + lengthA > start + length) { - newLength = start + length - newStart; - } else { - newLength = (Guint)lengthA; - } - subStr = new MemStream(buf, newStart, newLength, dictA); - return subStr; -} - -void MemStream::reset() { - bufPtr = buf + start; -} - -void MemStream::close() { -} - -int MemStream::getBlock(char *blk, int size) { - int n; - - if (size <= 0) { - return 0; - } - if (bufEnd - bufPtr < size) { - n = (int)(bufEnd - bufPtr); - } else { - n = size; - } - memcpy(blk, bufPtr, n); - bufPtr += n; - return n; -} - -void MemStream::setPos(GFileOffset pos, int dir) { - Guint i; - - if (dir >= 0) { - i = (Guint)pos; - } else { - i = (Guint)(start + length - pos); - } - if (i < start) { - i = start; - } else if (i > start + length) { - i = start + length; - } - bufPtr = buf + i; -} - -void MemStream::moveStart(int delta) { - start += delta; - length -= delta; - bufPtr = buf + start; -} - -//------------------------------------------------------------------------ -// EmbedStream -//------------------------------------------------------------------------ - -EmbedStream::EmbedStream(Stream *strA, Object *dictA, - GBool limitedA, GFileOffset lengthA): - BaseStream(dictA) { - str = strA; - limited = limitedA; - length = lengthA; -} - -EmbedStream::~EmbedStream() { -} - -Stream *EmbedStream::copy() { - Object dictA; - - dict.copy(&dictA); - return new EmbedStream(str, &dictA, limited, length); -} - -Stream *EmbedStream::makeSubStream(GFileOffset start, GBool limitedA, - GFileOffset lengthA, Object *dictA) { - error(errInternal, -1, "Called makeSubStream() on EmbedStream"); - return NULL; -} - -int EmbedStream::getChar() { - if (limited && !length) { - return EOF; - } - --length; - return str->getChar(); -} - -int EmbedStream::lookChar() { - if (limited && !length) { - return EOF; - } - return str->lookChar(); -} - -int EmbedStream::getBlock(char *blk, int size) { - if (size <= 0) { - return 0; - } - if (limited && length < (Guint)size) { - size = (int)length; - } - length -= size; - return str->getBlock(blk, size); -} - -void EmbedStream::setPos(GFileOffset pos, int dir) { - error(errInternal, -1, "Called setPos() on EmbedStream"); -} - -GFileOffset EmbedStream::getStart() { - error(errInternal, -1, "Called getStart() on EmbedStream"); - return 0; -} - -void EmbedStream::moveStart(int delta) { - error(errInternal, -1, "Called moveStart() on EmbedStream"); -} - -//------------------------------------------------------------------------ -// ASCIIHexStream -//------------------------------------------------------------------------ - -ASCIIHexStream::ASCIIHexStream(Stream *strA): - FilterStream(strA) { - buf = EOF; - eof = gFalse; -} - -ASCIIHexStream::~ASCIIHexStream() { - delete str; -} - -Stream *ASCIIHexStream::copy() { - return new ASCIIHexStream(str->copy()); -} - -void ASCIIHexStream::reset() { - str->reset(); - buf = EOF; - eof = gFalse; -} - -int ASCIIHexStream::lookChar() { - int c1, c2, x; - - if (buf != EOF) - return buf; - if (eof) { - buf = EOF; - return EOF; - } - do { - c1 = str->getChar(); - } while (isspace(c1)); - if (c1 == '>') { - eof = gTrue; - buf = EOF; - return buf; - } - do { - c2 = str->getChar(); - } while (isspace(c2)); - if (c2 == '>') { - eof = gTrue; - c2 = '0'; - } - if (c1 >= '0' && c1 <= '9') { - x = (c1 - '0') << 4; - } else if (c1 >= 'A' && c1 <= 'F') { - x = (c1 - 'A' + 10) << 4; - } else if (c1 >= 'a' && c1 <= 'f') { - x = (c1 - 'a' + 10) << 4; - } else if (c1 == EOF) { - eof = gTrue; - x = 0; - } else { - error(errSyntaxError, getPos(), - "Illegal character <{0:02x}> in ASCIIHex stream", c1); - x = 0; - } - if (c2 >= '0' && c2 <= '9') { - x += c2 - '0'; - } else if (c2 >= 'A' && c2 <= 'F') { - x += c2 - 'A' + 10; - } else if (c2 >= 'a' && c2 <= 'f') { - x += c2 - 'a' + 10; - } else if (c2 == EOF) { - eof = gTrue; - x = 0; - } else { - error(errSyntaxError, getPos(), - "Illegal character <{0:02x}> in ASCIIHex stream", c2); - } - buf = x & 0xff; - return buf; -} - -GString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 2) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("/ASCIIHexDecode filter\n"); - return s; -} - -GBool ASCIIHexStream::isBinary(GBool last) { - return str->isBinary(gFalse); -} - -//------------------------------------------------------------------------ -// ASCII85Stream -//------------------------------------------------------------------------ - -ASCII85Stream::ASCII85Stream(Stream *strA): - FilterStream(strA) { - index = n = 0; - eof = gFalse; -} - -ASCII85Stream::~ASCII85Stream() { - delete str; -} - -Stream *ASCII85Stream::copy() { - return new ASCII85Stream(str->copy()); -} - -void ASCII85Stream::reset() { - str->reset(); - index = n = 0; - eof = gFalse; -} - -int ASCII85Stream::lookChar() { - int k; - Gulong t; - - if (index >= n) { - if (eof) - return EOF; - index = 0; - do { - c[0] = str->getChar(); - } while (Lexer::isSpace(c[0])); - if (c[0] == '~' || c[0] == EOF) { - eof = gTrue; - n = 0; - return EOF; - } else if (c[0] == 'z') { - b[0] = b[1] = b[2] = b[3] = 0; - n = 4; - } else { - for (k = 1; k < 5; ++k) { - do { - c[k] = str->getChar(); - } while (Lexer::isSpace(c[k])); - if (c[k] == '~' || c[k] == EOF) - break; - } - n = k - 1; - if (k < 5 && (c[k] == '~' || c[k] == EOF)) { - for (++k; k < 5; ++k) - c[k] = 0x21 + 84; - eof = gTrue; - } - t = 0; - for (k = 0; k < 5; ++k) - t = t * 85 + (c[k] - 0x21); - for (k = 3; k >= 0; --k) { - b[k] = (int)(t & 0xff); - t >>= 8; - } - } - } - return b[index]; -} - -GString *ASCII85Stream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 2) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("/ASCII85Decode filter\n"); - return s; -} - -GBool ASCII85Stream::isBinary(GBool last) { - return str->isBinary(gFalse); -} - -//------------------------------------------------------------------------ -// LZWStream -//------------------------------------------------------------------------ - -LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors, - int bits, int earlyA): - FilterStream(strA) { - if (predictor != 1) { - pred = new StreamPredictor(this, predictor, columns, colors, bits); - if (!pred->isOk()) { - delete pred; - pred = NULL; - } - } else { - pred = NULL; - } - early = earlyA; - eof = gFalse; - inputBits = 0; - clearTable(); -} - -LZWStream::~LZWStream() { - if (pred) { - delete pred; - } - delete str; -} - -Stream *LZWStream::copy() { - if (pred) { - return new LZWStream(str->copy(), pred->getPredictor(), - pred->getWidth(), pred->getNComps(), - pred->getNBits(), early); - } else { - return new LZWStream(str->copy(), 1, 0, 0, 0, early); - } -} - -int LZWStream::getChar() { - if (pred) { - return pred->getChar(); - } - if (eof) { - return EOF; - } - if (seqIndex >= seqLength) { - if (!processNextCode()) { - return EOF; - } - } - return seqBuf[seqIndex++]; -} - -int LZWStream::lookChar() { - if (pred) { - return pred->lookChar(); - } - if (eof) { - return EOF; - } - if (seqIndex >= seqLength) { - if (!processNextCode()) { - return EOF; - } - } - return seqBuf[seqIndex]; -} - -int LZWStream::getRawChar() { - if (eof) { - return EOF; - } - if (seqIndex >= seqLength) { - if (!processNextCode()) { - return EOF; - } - } - return seqBuf[seqIndex++]; -} - -int LZWStream::getBlock(char *blk, int size) { - int n, m; - - if (pred) { - return pred->getBlock(blk, size); - } - if (eof) { - return 0; - } - n = 0; - while (n < size) { - if (seqIndex >= seqLength) { - if (!processNextCode()) { - break; - } - } - m = seqLength - seqIndex; - if (m > size - n) { - m = size - n; - } - memcpy(blk + n, seqBuf + seqIndex, m); - seqIndex += m; - n += m; - } - return n; -} - -void LZWStream::reset() { - str->reset(); - if (pred) { - pred->reset(); - } - eof = gFalse; - inputBits = 0; - clearTable(); -} - -GBool LZWStream::processNextCode() { - int code; - int nextLength; - int i, j; - - // check for EOF - if (eof) { - return gFalse; - } - - // check for eod and clear-table codes - start: - code = getCode(); - if (code == EOF || code == 257) { - eof = gTrue; - return gFalse; - } - if (code == 256) { - clearTable(); - goto start; - } - if (nextCode >= 4097) { - error(errSyntaxError, getPos(), - "Bad LZW stream - expected clear-table code"); - clearTable(); - } - - // process the next code - nextLength = seqLength + 1; - if (code < 256) { - seqBuf[0] = (Guchar)code; - seqLength = 1; - } else if (code < nextCode) { - seqLength = table[code].length; - for (i = seqLength - 1, j = code; i > 0; --i) { - seqBuf[i] = table[j].tail; - j = table[j].head; - } - seqBuf[0] = (Guchar)j; - } else if (code == nextCode) { - seqBuf[seqLength] = (Guchar)newChar; - ++seqLength; - } else { - error(errSyntaxError, getPos(), "Bad LZW stream - unexpected code"); - eof = gTrue; - return gFalse; - } - newChar = seqBuf[0]; - if (first) { - first = gFalse; - } else { - table[nextCode].length = nextLength; - table[nextCode].head = prevCode; - table[nextCode].tail = (Guchar)newChar; - ++nextCode; - if (nextCode + early == 512) - nextBits = 10; - else if (nextCode + early == 1024) - nextBits = 11; - else if (nextCode + early == 2048) - nextBits = 12; - } - prevCode = code; - - // reset buffer - seqIndex = 0; - - return gTrue; -} - -void LZWStream::clearTable() { - nextCode = 258; - nextBits = 9; - seqIndex = seqLength = 0; - first = gTrue; -} - -int LZWStream::getCode() { - int c; - int code; - - while (inputBits < nextBits) { - if ((c = str->getChar()) == EOF) - return EOF; - inputBuf = (inputBuf << 8) | (c & 0xff); - inputBits += 8; - } - code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1); - inputBits -= nextBits; - return code; -} - -GString *LZWStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 2 || pred) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("<< "); - if (!early) { - s->append("/EarlyChange 0 "); - } - s->append(">> /LZWDecode filter\n"); - return s; -} - -GBool LZWStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -//------------------------------------------------------------------------ -// RunLengthStream -//------------------------------------------------------------------------ - -RunLengthStream::RunLengthStream(Stream *strA): - FilterStream(strA) { - bufPtr = bufEnd = buf; - eof = gFalse; -} - -RunLengthStream::~RunLengthStream() { - delete str; -} - -Stream *RunLengthStream::copy() { - return new RunLengthStream(str->copy()); -} - -void RunLengthStream::reset() { - str->reset(); - bufPtr = bufEnd = buf; - eof = gFalse; -} - -int RunLengthStream::getBlock(char *blk, int size) { - int n, m; - - n = 0; - while (n < size) { - if (bufPtr >= bufEnd) { - if (!fillBuf()) { - break; - } - } - m = (int)(bufEnd - bufPtr); - if (m > size - n) { - m = size - n; - } - memcpy(blk + n, bufPtr, m); - bufPtr += m; - n += m; - } - return n; -} - -GString *RunLengthStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 2) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("/RunLengthDecode filter\n"); - return s; -} - -GBool RunLengthStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -GBool RunLengthStream::fillBuf() { - int c; - int n, i; - - if (eof) - return gFalse; - c = str->getChar(); - if (c == 0x80 || c == EOF) { - eof = gTrue; - return gFalse; - } - if (c < 0x80) { - n = c + 1; - for (i = 0; i < n; ++i) - buf[i] = (char)str->getChar(); - } else { - n = 0x101 - c; - c = str->getChar(); - for (i = 0; i < n; ++i) - buf[i] = (char)c; - } - bufPtr = buf; - bufEnd = buf + n; - return gTrue; -} - -//------------------------------------------------------------------------ -// CCITTFaxStream -//------------------------------------------------------------------------ - -CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, - GBool byteAlignA, int columnsA, int rowsA, - GBool endOfBlockA, GBool blackA): - FilterStream(strA) { - encoding = encodingA; - endOfLine = endOfLineA; - byteAlign = byteAlignA; - columns = columnsA; - if (columns < 1) { - columns = 1; - } else if (columns > INT_MAX - 3) { - columns = INT_MAX - 3; - } - rows = rowsA; - endOfBlock = endOfBlockA; - black = blackA; - blackXOR = black ? 0xff : 0x00; - // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = columns - // ---> max codingLine size = columns + 1 - // refLine has two extra guard entries at the end - // ---> max refLine size = columns + 3 - codingLine = (int *)gmallocn(columns + 1, sizeof(int)); - refLine = (int *)gmallocn(columns + 3, sizeof(int)); - - eof = gFalse; - row = 0; - nextLine2D = encoding < 0; - inputBits = 0; - codingLine[0] = columns; - nextCol = columns; - a0i = 0; - err = gFalse; - nErrors = 0; -} - -CCITTFaxStream::~CCITTFaxStream() { - delete str; - gfree(refLine); - gfree(codingLine); -} - -Stream *CCITTFaxStream::copy() { - return new CCITTFaxStream(str->copy(), encoding, endOfLine, - byteAlign, columns, rows, endOfBlock, black); -} - -void CCITTFaxStream::reset() { - int code1; - - str->reset(); - eof = gFalse; - row = 0; - nextLine2D = encoding < 0; - inputBits = 0; - codingLine[0] = columns; - nextCol = columns; - a0i = 0; - - // skip any initial zero bits and end-of-line marker, and get the 2D - // encoding tag - while ((code1 = lookBits(12)) == 0) { - eatBits(1); - } - if (code1 == 0x001) { - eatBits(12); - endOfLine = gTrue; - } - if (encoding > 0) { - nextLine2D = !lookBits(1); - eatBits(1); - } -} - -int CCITTFaxStream::getChar() { - int c, bitsNeeded, bitsAvail, bitsUsed; - - if (nextCol >= columns) { - if (eof) { - return EOF; - } - if (!readRow()) { - return EOF; - } - } - bitsAvail = codingLine[a0i] - nextCol; - if (bitsAvail > 8) { - c = (a0i & 1) ? 0x00 : 0xff; - } else { - c = 0; - bitsNeeded = 8; - do { - bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; - c <<= bitsUsed; - if (!(a0i & 1)) { - c |= 0xff >> (8 - bitsUsed); - } - bitsAvail -= bitsUsed; - bitsNeeded -= bitsUsed; - if (bitsAvail == 0) { - if (codingLine[a0i] >= columns) { - c <<= bitsNeeded; - break; - } - ++a0i; - bitsAvail = codingLine[a0i] - codingLine[a0i - 1]; - } - } while (bitsNeeded > 0); - } - nextCol += 8; - c ^= blackXOR; - return c; -} - -int CCITTFaxStream::lookChar() { - int c, bitsNeeded, bitsAvail, bitsUsed, i; - - if (nextCol >= columns) { - if (eof) { - return EOF; - } - if (!readRow()) { - return EOF; - } - } - bitsAvail = codingLine[a0i] - nextCol; - if (bitsAvail >= 8) { - c = (a0i & 1) ? 0x00 : 0xff; - } else { - i = a0i; - c = 0; - bitsNeeded = 8; - do { - bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; - c <<= bitsUsed; - if (!(i & 1)) { - c |= 0xff >> (8 - bitsUsed); - } - bitsAvail -= bitsUsed; - bitsNeeded -= bitsUsed; - if (bitsAvail == 0) { - if (codingLine[i] >= columns) { - c <<= bitsNeeded; - break; - } - ++i; - bitsAvail = codingLine[i] - codingLine[i - 1]; - } - } while (bitsNeeded > 0); - } - c ^= blackXOR; - return c; -} - -int CCITTFaxStream::getBlock(char *blk, int size) { - int bytesRead, bitsAvail, bitsNeeded, bitsUsed, byte, c; - - bytesRead = 0; - while (bytesRead < size) { - if (nextCol >= columns) { - if (eof) { - break; - } - if (!readRow()) { - break; - } - } - bitsAvail = codingLine[a0i] - nextCol; - byte = (a0i & 1) ? 0x00 : 0xff; - if (bitsAvail > 8) { - c = byte; - bitsAvail -= 8; - } else { - c = 0; - bitsNeeded = 8; - do { - bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; - c <<= bitsUsed; - c |= byte >> (8 - bitsUsed); - bitsAvail -= bitsUsed; - bitsNeeded -= bitsUsed; - if (bitsAvail == 0) { - if (codingLine[a0i] >= columns) { - c <<= bitsNeeded; - break; - } - ++a0i; - bitsAvail = codingLine[a0i] - codingLine[a0i - 1]; - byte ^= 0xff; - } - } while (bitsNeeded > 0); - } - nextCol += 8; - blk[bytesRead++] = (char)(c ^ blackXOR); - } - return bytesRead; -} - -inline void CCITTFaxStream::addPixels(int a1, int blackPixels) { - if (a1 > codingLine[a0i]) { - if (a1 > columns) { - error(errSyntaxError, getPos(), - "CCITTFax row is wrong length ({0:d})", a1); - err = gTrue; - ++nErrors; - a1 = columns; - } - if ((a0i & 1) ^ blackPixels) { - ++a0i; - } - codingLine[a0i] = a1; - } -} - -inline void CCITTFaxStream::addPixelsNeg(int a1, int blackPixels) { - if (a1 > codingLine[a0i]) { - if (a1 > columns) { - error(errSyntaxError, getPos(), - "CCITTFax row is wrong length ({0:d})", a1); - err = gTrue; - ++nErrors; - a1 = columns; - } - if ((a0i & 1) ^ blackPixels) { - ++a0i; - } - codingLine[a0i] = a1; - } else if (a1 < codingLine[a0i]) { - if (a1 < 0) { - error(errSyntaxError, getPos(), "Invalid CCITTFax code"); - err = gTrue; - ++nErrors; - a1 = 0; - } - while (a0i > 0 && a1 <= codingLine[a0i - 1]) { - --a0i; - } - codingLine[a0i] = a1; - } -} - -GBool CCITTFaxStream::readRow() { - int code1, code2, code3; - int b1i, blackPixels, i; - GBool gotEOL; - - // if at eof just return EOF - if (eof) { - return gFalse; - } - - err = gFalse; - - // 2-D encoding - if (nextLine2D) { - for (i = 0; codingLine[i] < columns; ++i) { - refLine[i] = codingLine[i]; - } - refLine[i++] = columns; - refLine[i++] = columns; - refLine[i] = columns; - codingLine[0] = 0; - a0i = 0; - b1i = 0; - blackPixels = 0; - // invariant: - // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] - // <= columns - // exception at left edge: - // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible - // exception at right edge: - // refLine[b1i] = refLine[b1i+1] = columns is possible - while (codingLine[a0i] < columns) { - code1 = getTwoDimCode(); - switch (code1) { - case twoDimPass: - addPixels(refLine[b1i + 1], blackPixels); - if (refLine[b1i + 1] < columns) { - b1i += 2; - } - break; - case twoDimHoriz: - code1 = code2 = 0; - if (blackPixels) { - do { - code1 += code3 = getBlackCode(); - } while (code3 >= 64); - do { - code2 += code3 = getWhiteCode(); - } while (code3 >= 64); - } else { - do { - code1 += code3 = getWhiteCode(); - } while (code3 >= 64); - do { - code2 += code3 = getBlackCode(); - } while (code3 >= 64); - } - addPixels(codingLine[a0i] + code1, blackPixels); - if (codingLine[a0i] < columns) { - addPixels(codingLine[a0i] + code2, blackPixels ^ 1); - } - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - break; - case twoDimVertR3: - addPixels(refLine[b1i] + 3, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - ++b1i; - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVertR2: - addPixels(refLine[b1i] + 2, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - ++b1i; - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVertR1: - addPixels(refLine[b1i] + 1, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - ++b1i; - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVert0: - addPixels(refLine[b1i], blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - ++b1i; - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVertL3: - addPixelsNeg(refLine[b1i] - 3, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - if (b1i > 0) { - --b1i; - } else { - ++b1i; - } - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVertL2: - addPixelsNeg(refLine[b1i] - 2, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - if (b1i > 0) { - --b1i; - } else { - ++b1i; - } - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVertL1: - addPixelsNeg(refLine[b1i] - 1, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - if (b1i > 0) { - --b1i; - } else { - ++b1i; - } - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case EOF: - addPixels(columns, 0); - err = gTrue; - break; - default: - error(errSyntaxError, getPos(), - "Bad 2D code {0:04x} in CCITTFax stream", code1); - addPixels(columns, 0); - err = gTrue; - ++nErrors; - break; - } - } - - // 1-D encoding - } else { - codingLine[0] = 0; - a0i = 0; - blackPixels = 0; - while (codingLine[a0i] < columns) { - code1 = 0; - if (blackPixels) { - do { - code1 += code3 = getBlackCode(); - } while (code3 >= 64); - } else { - do { - code1 += code3 = getWhiteCode(); - } while (code3 >= 64); - } - addPixels(codingLine[a0i] + code1, blackPixels); - blackPixels ^= 1; - } - } - - // check for end-of-line marker, skipping over any extra zero bits - // (if EncodedByteAlign is true and EndOfLine is false, there can - // be "false" EOL markers -- i.e., if the last n unused bits in - // row i are set to zero, and the first 11-n bits in row i+1 - // happen to be zero -- so we don't look for EOL markers in this - // case) - gotEOL = gFalse; - if (!endOfBlock && row == rows - 1) { - eof = gTrue; - } else if (endOfLine || !byteAlign) { - code1 = lookBits(12); - if (endOfLine) { - while (code1 != EOF && code1 != 0x001) { - eatBits(1); - code1 = lookBits(12); - } - } else { - while (code1 == 0) { - eatBits(1); - code1 = lookBits(12); - } - } - if (code1 == 0x001) { - eatBits(12); - gotEOL = gTrue; - } - } - - // byte-align the row - // (Adobe apparently doesn't do byte alignment after EOL markers - // -- I've seen CCITT image data streams in two different formats, - // both with the byteAlign flag set: - // 1. xx:x0:01:yy:yy - // 2. xx:00:1y:yy:yy - // where xx is the previous line, yy is the next line, and colons - // separate bytes.) - if (byteAlign && !gotEOL) { - inputBits &= ~7; - } - - // check for end of stream - if (lookBits(1) == EOF) { - eof = gTrue; - } - - // get 2D encoding tag - if (!eof && encoding > 0) { - nextLine2D = !lookBits(1); - eatBits(1); - } - - // check for end-of-block marker - if (endOfBlock && !endOfLine && byteAlign) { - // in this case, we didn't check for an EOL code above, so we - // need to check here - code1 = lookBits(24); - if (code1 == 0x001001) { - eatBits(12); - gotEOL = gTrue; - } - } - if (endOfBlock && gotEOL) { - code1 = lookBits(12); - if (code1 == 0x001) { - eatBits(12); - if (encoding > 0) { - lookBits(1); - eatBits(1); - } - if (encoding >= 0) { - for (i = 0; i < 4; ++i) { - code1 = lookBits(12); - if (code1 != 0x001) { - error(errSyntaxError, getPos(), - "Bad RTC code in CCITTFax stream"); - ++nErrors; - } - eatBits(12); - if (encoding > 0) { - lookBits(1); - eatBits(1); - } - } - } - eof = gTrue; - } - - // look for an end-of-line marker after an error -- we only do - // this if we know the stream contains end-of-line markers because - // the "just plow on" technique tends to work better otherwise - } else if (err && endOfLine) { - while (1) { - code1 = lookBits(13); - if (code1 == EOF) { - eof = gTrue; - return gFalse; - } - if ((code1 >> 1) == 0x001) { - break; - } - eatBits(1); - } - eatBits(12); - if (encoding > 0) { - eatBits(1); - nextLine2D = !(code1 & 1); - } - } - - // corrupt CCITTFax streams can generate huge data expansion -- we - // avoid that case by aborting decode after 1000 errors - if (nErrors > 1000) { - error(errSyntaxError, getPos(), "Too many errors in CCITTFaxStream - aborting decode"); - eof = gTrue; - return gFalse; - } - - // set up for output - nextCol = 0; - a0i = (codingLine[0] > 0) ? 0 : 1; - - ++row; - - return gTrue; -} - -short CCITTFaxStream::getTwoDimCode() { - int code; - CCITTCode *p; - int n; - - code = 0; // make gcc happy - if (endOfBlock) { - if ((code = lookBits(7)) != EOF) { - p = &twoDimTab1[code]; - if (p->bits > 0) { - eatBits(p->bits); - return p->n; - } - } - } else { - for (n = 1; n <= 7; ++n) { - if ((code = lookBits(n)) == EOF) { - break; - } - if (n < 7) { - code <<= 7 - n; - } - p = &twoDimTab1[code]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - } - error(errSyntaxError, getPos(), - "Bad two dim code ({0:04x}) in CCITTFax stream", code); - ++nErrors; - return EOF; -} - -short CCITTFaxStream::getWhiteCode() { - short code; - CCITTCode *p; - int n; - - code = 0; // make gcc happy - if (endOfBlock) { - code = lookBits(12); - if (code == EOF) { - return 1; - } - if ((code >> 5) == 0) { - p = &whiteTab1[code]; - } else { - p = &whiteTab2[code >> 3]; - } - if (p->bits > 0) { - eatBits(p->bits); - return p->n; - } - } else { - for (n = 1; n <= 9; ++n) { - code = lookBits(n); - if (code == EOF) { - return 1; - } - if (n < 9) { - code = (short)(code << (9 - n)); - } - p = &whiteTab2[code]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - for (n = 11; n <= 12; ++n) { - code = lookBits(n); - if (code == EOF) { - return 1; - } - if (n < 12) { - code = (short)(code << (12 - n)); - } - p = &whiteTab1[code]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - } - error(errSyntaxError, getPos(), - "Bad white code ({0:04x}) in CCITTFax stream", code); - ++nErrors; - // eat a bit and return a positive number so that the caller doesn't - // go into an infinite loop - eatBits(1); - return 1; -} - -short CCITTFaxStream::getBlackCode() { - short code; - CCITTCode *p; - int n; - - code = 0; // make gcc happy - if (endOfBlock) { - code = lookBits(13); - if (code == EOF) { - return 1; - } - if ((code >> 7) == 0) { - p = &blackTab1[code]; - } else if ((code >> 9) == 0 && (code >> 7) != 0) { - p = &blackTab2[(code >> 1) - 64]; - } else { - p = &blackTab3[code >> 7]; - } - if (p->bits > 0) { - eatBits(p->bits); - return p->n; - } - } else { - for (n = 2; n <= 6; ++n) { - code = lookBits(n); - if (code == EOF) { - return 1; - } - if (n < 6) { - code = (short)(code << (6 - n)); - } - p = &blackTab3[code]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - for (n = 7; n <= 12; ++n) { - code = lookBits(n); - if (code == EOF) { - return 1; - } - if (n < 12) { - code = (short)(code << (12 - n)); - } - if (code >= 64) { - p = &blackTab2[code - 64]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - } - for (n = 10; n <= 13; ++n) { - code = lookBits(n); - if (code == EOF) { - return 1; - } - if (n < 13) { - code = (short)(code << (13 - n)); - } - p = &blackTab1[code]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - } - error(errSyntaxError, getPos(), - "Bad black code ({0:04x}) in CCITTFax stream", code); - ++nErrors; - // eat a bit and return a positive number so that the caller doesn't - // go into an infinite loop - eatBits(1); - return 1; -} - -short CCITTFaxStream::lookBits(int n) { - int c; - - while (inputBits < n) { - if ((c = str->getChar()) == EOF) { - if (inputBits == 0) { - return EOF; - } - // near the end of the stream, the caller may ask for more bits - // than are available, but there may still be a valid code in - // however many bits are available -- we need to return correct - // data in this case - return (short)((inputBuf << (n - inputBits)) & (0xffffffff >> (32 - n))); - } - inputBuf = (inputBuf << 8) + c; - inputBits += 8; - } - return (short)((inputBuf >> (inputBits - n)) & (0xffffffff >> (32 - n))); -} - -GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - char s1[50]; - - if (psLevel < 2) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("<< "); - if (encoding != 0) { - sprintf(s1, "/K %d ", encoding); - s->append(s1); - } - if (endOfLine) { - s->append("/EndOfLine true "); - } - if (byteAlign) { - s->append("/EncodedByteAlign true "); - } - sprintf(s1, "/Columns %d ", columns); - s->append(s1); - if (rows != 0) { - sprintf(s1, "/Rows %d ", rows); - s->append(s1); - } - if (!endOfBlock) { - s->append("/EndOfBlock false "); - } - if (black) { - s->append("/BlackIs1 true "); - } - s->append(">> /CCITTFaxDecode filter\n"); - return s; -} - -GBool CCITTFaxStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -//------------------------------------------------------------------------ -// DCTStream -//------------------------------------------------------------------------ - -#if HAVE_JPEGLIB - -DCTStream::DCTStream(Stream *strA, GBool colorXformA): - FilterStream(strA) { - colorXform = colorXformA; - lineBuf = NULL; - inlineImage = str->isEmbedStream(); -} - -DCTStream::~DCTStream() { - delete str; -} - -Stream *DCTStream::copy() { - return new DCTStream(str->copy(), colorXform); -} - -void DCTStream::reset() { - int i; - - lineBuf = NULL; - error = gFalse; - - str->reset(); - - // initialize the libjpeg decompression object - decomp.err = jpeg_std_error(&errorMgr.err); - errorMgr.err.error_exit = &errorExit; - errorMgr.err.output_message = &errorMessage; - if (setjmp(errorMgr.setjmpBuf)) { - error = gTrue; - return; - } - jpeg_create_decompress(&decomp); - - // set up the data source manager - sourceMgr.src.next_input_byte = NULL; - sourceMgr.src.bytes_in_buffer = 0; - sourceMgr.src.init_source = &initSourceCbk; - sourceMgr.src.fill_input_buffer = &fillInputBufferCbk; - sourceMgr.src.skip_input_data = &skipInputDataCbk; - sourceMgr.src.resync_to_restart = &jpeg_resync_to_restart; - sourceMgr.src.term_source = &termSourceCbk; - sourceMgr.str = this; - decomp.src = &sourceMgr.src; - - // read the header - jpeg_read_header(&decomp, TRUE); - jpeg_calc_output_dimensions(&decomp); - - // set up the color transform - if (!decomp.saw_Adobe_marker && colorXform >= 0) { - if (decomp.num_components == 3) { - decomp.jpeg_color_space = colorXform ? JCS_YCbCr : JCS_RGB; - decomp.out_color_space = JCS_RGB; - decomp.out_color_components = 3; - } else if (decomp.num_components == 4) { - decomp.jpeg_color_space = colorXform ? JCS_YCCK : JCS_CMYK; - decomp.out_color_space = JCS_CMYK; - decomp.out_color_components = 4; - } - } - - // allocate a line buffer - if ((lineBufHeight = decomp.rec_outbuf_height) > 4) { - lineBufHeight = 4; - } - lineBuf = (char *)gmallocn(lineBufHeight * decomp.out_color_components, - decomp.output_width); - for (i = 0; i < lineBufHeight; ++i) { - lineBufRows[i] = lineBuf + - i * decomp.out_color_components * decomp.output_width; - } - bufPtr = bufEnd = lineBuf; - - // start up the decompression process - jpeg_start_decompress(&decomp); -} - -void DCTStream::close() { - // we don't call jpeg_finish_decompress() here because it will report - // an error if the full image wasn't read - if (setjmp(errorMgr.setjmpBuf)) { - goto skip; - } - jpeg_destroy_decompress(&decomp); - skip: - gfree(lineBuf); - FilterStream::close(); -} - -int DCTStream::getChar() { - if (error) { - return EOF; - } - if (bufPtr == bufEnd) { - if (!fillBuf()) { - return EOF; - } - } - return *bufPtr++ & 0xff; -} - -int DCTStream::lookChar() { - if (error) { - return EOF; - } - if (bufPtr == bufEnd) { - if (!fillBuf()) { - return EOF; - } - } - return *bufPtr & 0xff; -} - -int DCTStream::getBlock(char *blk, int size) { - int nRead, nAvail, n; - - if (error) { - return 0; - } - nRead = 0; - while (nRead < size) { - if (bufPtr == bufEnd) { - if (!fillBuf()) { - break; - } - } - nAvail = bufEnd - bufPtr; - n = (nAvail < size - nRead) ? nAvail : size - nRead; - memcpy(blk + nRead, bufPtr, n); - bufPtr += n; - nRead += n; - } - return nRead; -} - -GBool DCTStream::fillBuf() { - int nLines; - - if (setjmp(errorMgr.setjmpBuf)) { - error = gTrue; - return gFalse; - } - nLines = jpeg_read_scanlines(&decomp, (JSAMPARRAY)lineBufRows, - lineBufHeight); - bufPtr = lineBuf; - bufEnd = lineBuf + - nLines * decomp.out_color_components * decomp.output_width; - return nLines > 0; -} - -void DCTStream::errorExit(j_common_ptr d) { - DCTErrorMgr *errMgr = (DCTErrorMgr *)d->err; - longjmp(errMgr->setjmpBuf, 1); -} - -void DCTStream::errorMessage(j_common_ptr d) { -#if 0 // for debugging - char buf[JMSG_LENGTH_MAX]; - - (*d->err->format_message)(d, buf); - fprintf(stderr, "%s\n", buf); -#endif -} - -void DCTStream::initSourceCbk(j_decompress_ptr d) { - DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; - - sourceMgr->src.next_input_byte = NULL; - sourceMgr->src.bytes_in_buffer = 0; -} - -boolean DCTStream::fillInputBufferCbk(j_decompress_ptr d) { - DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; - int c, n; - - // for inline images, we need to read one byte at a time so we don't - // read past the end of the input data - if (sourceMgr->str->inlineImage) { - c = sourceMgr->str->str->getChar(); - if (c == EOF) { - sourceMgr->buf[0] = (char)0xff; - sourceMgr->buf[1] = (char)JPEG_EOI; - sourceMgr->src.bytes_in_buffer = 2; - } else { - sourceMgr->buf[0] = (char)c; - sourceMgr->src.bytes_in_buffer = 1; - } - } else { - n = sourceMgr->str->str->getBlock(sourceMgr->buf, dctStreamBufSize); - if (n > 0) { - sourceMgr->src.bytes_in_buffer = (size_t)n; - } else { - sourceMgr->buf[0] = (char)0xff; - sourceMgr->buf[1] = (char)JPEG_EOI; - sourceMgr->src.bytes_in_buffer = 2; - } - } - sourceMgr->src.next_input_byte = (JOCTET *)sourceMgr->buf; - return TRUE; -} - -void DCTStream::skipInputDataCbk(j_decompress_ptr d, long numBytes) { - DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; - - if (numBytes > 0) { - if ((long)sourceMgr->src.bytes_in_buffer < numBytes) { - sourceMgr->str->str->discardChars( - (Guint)(numBytes - sourceMgr->src.bytes_in_buffer)); - sourceMgr->src.bytes_in_buffer = 0; - } else { - sourceMgr->src.bytes_in_buffer -= numBytes; - sourceMgr->src.next_input_byte += numBytes; - } - } -} - -void DCTStream::termSourceCbk(j_decompress_ptr d) { -} - -#else // HAVE_JPEGLIB - -#define idctScaleA 1024 -#define idctScaleB 1138 -#define idctScaleC 1730 -#define idctScaleD 1609 -#define idctScaleE 1264 -#define idctScaleF 1922 -#define idctScaleG 1788 -#define idctScaleH 2923 -#define idctScaleI 2718 -#define idctScaleJ 2528 - -static int idctScaleMat[64] = { - idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB, - idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE, - idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF, - idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG, - idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB, - idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG, - idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF, - idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE -}; - -// color conversion parameters (16.16 fixed point format) -#define dctCrToR 91881 // 1.4020 -#define dctCbToG -22553 // -0.3441363 -#define dctCrToG -46802 // -0.71413636 -#define dctCbToB 116130 // 1.772 - -// The dctClip function clips signed integers to the [0,255] range. -// To handle valid DCT inputs, this must support an input range of at -// least [-256,511]. Invalid DCT inputs (e.g., from damaged PDF -// files) can result in arbitrary values, so we want to mask those -// out. We round the input range size up to a power of 2 (so we can -// use a bit mask), which gives us an input range of [-384,639]. The -// end result is: -// input output -// ---------- ------ -// <-384 X invalid inputs -> output is "don't care" -// -384..-257 0 invalid inputs, clipped -// -256..-1 0 valid inputs, need to be clipped -// 0..255 0..255 -// 256..511 255 valid inputs, need to be clipped -// 512..639 255 invalid inputs, clipped -// >=512 X invalid inputs -> output is "don't care" - -#define dctClipOffset 384 -#define dctClipMask 1023 -static Guchar dctClipData[1024]; - -static inline void dctClipInit() { - static int initDone = 0; - int i; - if (!initDone) { - for (i = -384; i < 0; ++i) { - dctClipData[dctClipOffset + i] = 0; - } - for (i = 0; i < 256; ++i) { - dctClipData[dctClipOffset + i] = (Guchar)i; - } - for (i = 256; i < 639; ++i) { - dctClipData[dctClipOffset + i] = 255; - } - initDone = 1; - } -} - -static inline Guchar dctClip(int x) { - return dctClipData[(dctClipOffset + x) & dctClipMask]; -} - -// zig zag decode map -static int dctZigZag[64] = { - 0, - 1, 8, - 16, 9, 2, - 3, 10, 17, 24, - 32, 25, 18, 11, 4, - 5, 12, 19, 26, 33, 40, - 48, 41, 34, 27, 20, 13, 6, - 7, 14, 21, 28, 35, 42, 49, 56, - 57, 50, 43, 36, 29, 22, 15, - 23, 30, 37, 44, 51, 58, - 59, 52, 45, 38, 31, - 39, 46, 53, 60, - 61, 54, 47, - 55, 62, - 63 -}; - -DCTStream::DCTStream(Stream *strA, GBool colorXformA): - FilterStream(strA) { - int i; - - colorXform = colorXformA; - progressive = interleaved = gFalse; - width = height = 0; - mcuWidth = mcuHeight = 0; - numComps = 0; - comp = 0; - x = y = 0; - for (i = 0; i < 4; ++i) { - frameBuf[i] = NULL; - } - rowBuf = NULL; - memset(dcHuffTables, 0, sizeof(dcHuffTables)); - memset(acHuffTables, 0, sizeof(acHuffTables)); - - dctClipInit(); -} - -DCTStream::~DCTStream() { - close(); - delete str; -} - -Stream *DCTStream::copy() { - return new DCTStream(str->copy(), colorXform); -} - -void DCTStream::reset() { - int i; - - str->reset(); - - progressive = interleaved = gFalse; - width = height = 0; - numComps = 0; - numQuantTables = 0; - numDCHuffTables = 0; - numACHuffTables = 0; - gotJFIFMarker = gFalse; - gotAdobeMarker = gFalse; - restartInterval = 0; - - if (!readHeader(gTrue)) { - // force an EOF condition - progressive = gTrue; - y = height; - return; - } - - // compute MCU size - if (numComps == 1) { - compInfo[0].hSample = compInfo[0].vSample = 1; - } - mcuWidth = compInfo[0].hSample; - mcuHeight = compInfo[0].vSample; - for (i = 1; i < numComps; ++i) { - if (compInfo[i].hSample > mcuWidth) { - mcuWidth = compInfo[i].hSample; - } - if (compInfo[i].vSample > mcuHeight) { - mcuHeight = compInfo[i].vSample; - } - } - mcuWidth *= 8; - mcuHeight *= 8; - - // figure out color transform - if (colorXform == -1) { - if (numComps == 3) { - if (gotJFIFMarker) { - colorXform = 1; - } else if (compInfo[0].id == 82 && compInfo[1].id == 71 && - compInfo[2].id == 66) { // ASCII "RGB" - colorXform = 0; - } else { - colorXform = 1; - } - } else { - colorXform = 0; - } - } - - if (progressive || !interleaved) { - - // allocate a buffer for the whole image - bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; - bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight; - if (bufWidth <= 0 || bufHeight <= 0 || - bufWidth > INT_MAX / bufWidth / (int)sizeof(int)) { - error(errSyntaxError, getPos(), "Invalid image size in DCT stream"); - y = height; - return; - } - for (i = 0; i < numComps; ++i) { - frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int)); - memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int)); - } - - // read the image data - do { - restartMarker = 0xd0; - restart(); - readScan(); - } while (readHeader(gFalse)); - - // decode - decodeImage(); - - // initialize counters - comp = 0; - x = 0; - y = 0; - - } else { - - if (scanInfo.numComps != numComps) { - error(errSyntaxError, getPos(), "Invalid scan in sequential DCT stream"); - y = height; - return; - } - - // allocate a buffer for one row of MCUs - bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; - rowBuf = (Guchar *)gmallocn(numComps * mcuHeight, bufWidth); - rowBufPtr = rowBufEnd = rowBuf; - - // initialize counters - y = -mcuHeight; - - restartMarker = 0xd0; - restart(); - } -} - -void DCTStream::close() { - int i; - - for (i = 0; i < 4; ++i) { - gfree(frameBuf[i]); - frameBuf[i] = NULL; - } - gfree(rowBuf); - rowBuf = NULL; - FilterStream::close(); -} - -int DCTStream::getChar() { - int c; - - if (progressive || !interleaved) { - if (y >= height) { - return EOF; - } - c = frameBuf[comp][y * bufWidth + x]; - if (++comp == numComps) { - comp = 0; - if (++x == width) { - x = 0; - ++y; - } - } - } else { - if (rowBufPtr == rowBufEnd) { - if (y + mcuHeight >= height) { - return EOF; - } - y += mcuHeight; - if (!readMCURow()) { - y = height; - return EOF; - } - } - c = *rowBufPtr++; - } - return c; -} - -int DCTStream::lookChar() { - if (progressive || !interleaved) { - if (y >= height) { - return EOF; - } - return frameBuf[comp][y * bufWidth + x]; - } else { - if (rowBufPtr == rowBufEnd) { - if (y + mcuHeight >= height) { - return EOF; - } - if (!readMCURow()) { - y = height; - return EOF; - } - } - return *rowBufPtr; - } -} - -int DCTStream::getBlock(char *blk, int size) { - int nRead, nAvail, n; - - if (progressive || !interleaved) { - if (y >= height) { - return 0; - } - for (nRead = 0; nRead < size; ++nRead) { - blk[nRead] = (char)frameBuf[comp][y * bufWidth + x]; - if (++comp == numComps) { - comp = 0; - if (++x == width) { - x = 0; - ++y; - if (y >= height) { - ++nRead; - break; - } - } - } - } - } else { - nRead = 0; - while (nRead < size) { - if (rowBufPtr == rowBufEnd) { - if (y + mcuHeight >= height) { - break; - } - y += mcuHeight; - if (!readMCURow()) { - y = height; - break; - } - } - nAvail = (int)(rowBufEnd - rowBufPtr); - n = (nAvail < size - nRead) ? nAvail : size - nRead; - memcpy(blk + nRead, rowBufPtr, n); - rowBufPtr += n; - nRead += n; - } - } - return nRead; -} - -void DCTStream::restart() { - int i; - - inputBits = 0; - restartCtr = restartInterval; - for (i = 0; i < numComps; ++i) { - compInfo[i].prevDC = 0; - } - eobRun = 0; -} - -// Read one row of MCUs from a sequential JPEG stream. -GBool DCTStream::readMCURow() { - int data1[64]; - Guchar data2[64]; - Guchar *p1, *p2; - int pY, pCb, pCr, pR, pG, pB; - int h, v, horiz, vert, hSub, vSub; - int x1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; - int c; - - for (cc = 0; cc < numComps; ++cc) { - if (scanInfo.dcHuffTable[cc] >= numDCHuffTables || - scanInfo.acHuffTable[cc] >= numACHuffTables) { - error(errSyntaxError, getPos(), - "Bad DCT data: invalid Huffman table index"); - return gFalse; - } - if (compInfo[cc].quantTable > numQuantTables) { - error(errSyntaxError, getPos(), - "Bad DCT data: invalid quant table index"); - return gFalse; - } - } - - for (x1 = 0; x1 < width; x1 += mcuWidth) { - - // deal with restart marker - if (restartInterval > 0 && restartCtr == 0) { - c = readMarker(); - if (c != restartMarker) { - error(errSyntaxError, getPos(), - "Bad DCT data: incorrect restart marker"); - return gFalse; - } - if (++restartMarker == 0xd8) - restartMarker = 0xd0; - restart(); - } - - // read one MCU - for (cc = 0; cc < numComps; ++cc) { - h = compInfo[cc].hSample; - v = compInfo[cc].vSample; - horiz = mcuWidth / h; - vert = mcuHeight / v; - hSub = horiz / 8; - vSub = vert / 8; - for (y2 = 0; y2 < mcuHeight; y2 += vert) { - for (x2 = 0; x2 < mcuWidth; x2 += horiz) { - if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], - &acHuffTables[scanInfo.acHuffTable[cc]], - &compInfo[cc].prevDC, - data1)) { - return gFalse; - } - transformDataUnit(quantTables[compInfo[cc].quantTable], - data1, data2); - if (hSub == 1 && vSub == 1 && x1+x2+8 <= width) { - for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { - p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc]; - p1[0] = data2[i]; - p1[ numComps] = data2[i+1]; - p1[2*numComps] = data2[i+2]; - p1[3*numComps] = data2[i+3]; - p1[4*numComps] = data2[i+4]; - p1[5*numComps] = data2[i+5]; - p1[6*numComps] = data2[i+6]; - p1[7*numComps] = data2[i+7]; - } - } else if (hSub == 2 && vSub == 2 && x1+x2+16 <= width) { - for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { - p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc]; - p2 = p1 + width * numComps; - p1[0] = p1[numComps] = - p2[0] = p2[numComps] = data2[i]; - p1[2*numComps] = p1[3*numComps] = - p2[2*numComps] = p2[3*numComps] = data2[i+1]; - p1[4*numComps] = p1[5*numComps] = - p2[4*numComps] = p2[5*numComps] = data2[i+2]; - p1[6*numComps] = p1[7*numComps] = - p2[6*numComps] = p2[7*numComps] = data2[i+3]; - p1[8*numComps] = p1[9*numComps] = - p2[8*numComps] = p2[9*numComps] = data2[i+4]; - p1[10*numComps] = p1[11*numComps] = - p2[10*numComps] = p2[11*numComps] = data2[i+5]; - p1[12*numComps] = p1[13*numComps] = - p2[12*numComps] = p2[13*numComps] = data2[i+6]; - p1[14*numComps] = p1[15*numComps] = - p2[14*numComps] = p2[15*numComps] = data2[i+7]; - } - } else { - p1 = &rowBuf[(y2 * width + (x1+x2)) * numComps + cc]; - i = 0; - for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { - for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { - for (y5 = 0; y5 < vSub; ++y5) { - for (x5 = 0; x5 < hSub && x1+x2+x4+x5 < width; ++x5) { - p1[((y4+y5) * width + (x4+x5)) * numComps] = data2[i]; - } - } - ++i; - } - } - } - } - } - } - --restartCtr; - } - - // color space conversion - if (colorXform) { - // convert YCbCr to RGB - if (numComps == 3) { - for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 3) { - pY = p1[0]; - pCb = p1[1] - 128; - pCr = p1[2] - 128; - pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; - p1[0] = dctClip(pR); - pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; - p1[1] = dctClip(pG); - pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; - p1[2] = dctClip(pB); - } - // convert YCbCrK to CMYK (K is passed through unchanged) - } else if (numComps == 4) { - for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 4) { - pY = p1[0]; - pCb = p1[1] - 128; - pCr = p1[2] - 128; - pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; - p1[0] = (Guchar)(255 - dctClip(pR)); - pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; - p1[1] = (Guchar)(255 - dctClip(pG)); - pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; - p1[2] = (Guchar)(255 - dctClip(pB)); - } - } - } - - rowBufPtr = rowBuf; - if (y + mcuHeight <= height) { - rowBufEnd = rowBuf + numComps * width * mcuHeight; - } else { - rowBufEnd = rowBuf + numComps * width * (height - y); - } - - return gTrue; -} - -// Read one scan from a progressive or non-interleaved JPEG stream. -void DCTStream::readScan() { - int data[64]; - int x1, y1, dx1, dy1, x2, y2, y3, cc, i; - int h, v, horiz, vert, vSub; - int *p1; - int c; - - for (cc = 0; cc < numComps; ++cc) { - if (scanInfo.comp[cc] && - (scanInfo.dcHuffTable[cc] >= numDCHuffTables || - ((!progressive || scanInfo.lastCoeff > 0) && - scanInfo.acHuffTable[cc] >= numACHuffTables))) { - error(errSyntaxError, getPos(), - "Bad DCT data: invalid Huffman table index"); - return; - } - if (compInfo[cc].quantTable > numQuantTables) { - error(errSyntaxError, getPos(), - "Bad DCT data: invalid quant table index"); - return; - } - } - - if (scanInfo.numComps == 1) { - for (cc = 0; cc < numComps; ++cc) { - if (scanInfo.comp[cc]) { - break; - } - } - dx1 = mcuWidth / compInfo[cc].hSample; - dy1 = mcuHeight / compInfo[cc].vSample; - } else { - dx1 = mcuWidth; - dy1 = mcuHeight; - } - - for (y1 = 0; y1 < height; y1 += dy1) { - for (x1 = 0; x1 < width; x1 += dx1) { - - // deal with restart marker - if (restartInterval > 0 && restartCtr == 0) { - c = readMarker(); - if (c != restartMarker) { - error(errSyntaxError, getPos(), - "Bad DCT data: incorrect restart marker"); - return; - } - if (++restartMarker == 0xd8) { - restartMarker = 0xd0; - } - restart(); - } - - // read one MCU - for (cc = 0; cc < numComps; ++cc) { - if (!scanInfo.comp[cc]) { - continue; - } - - h = compInfo[cc].hSample; - v = compInfo[cc].vSample; - horiz = mcuWidth / h; - vert = mcuHeight / v; - vSub = vert / 8; - for (y2 = 0; y2 < dy1; y2 += vert) { - for (x2 = 0; x2 < dx1; x2 += horiz) { - - // pull out the current values - p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; - for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { - data[i] = p1[0]; - data[i+1] = p1[1]; - data[i+2] = p1[2]; - data[i+3] = p1[3]; - data[i+4] = p1[4]; - data[i+5] = p1[5]; - data[i+6] = p1[6]; - data[i+7] = p1[7]; - p1 += bufWidth * vSub; - } - - // read one data unit - if (progressive) { - if (!readProgressiveDataUnit( - &dcHuffTables[scanInfo.dcHuffTable[cc]], - &acHuffTables[scanInfo.acHuffTable[cc]], - &compInfo[cc].prevDC, - data)) { - return; - } - } else { - if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], - &acHuffTables[scanInfo.acHuffTable[cc]], - &compInfo[cc].prevDC, - data)) { - return; - } - } - - // add the data unit into frameBuf - p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; - for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { - p1[0] = data[i]; - p1[1] = data[i+1]; - p1[2] = data[i+2]; - p1[3] = data[i+3]; - p1[4] = data[i+4]; - p1[5] = data[i+5]; - p1[6] = data[i+6]; - p1[7] = data[i+7]; - p1 += bufWidth * vSub; - } - } - } - } - --restartCtr; - } - } -} - -// Read one data unit from a sequential JPEG stream. -GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable, - DCTHuffTable *acHuffTable, - int *prevDC, int data[64]) { - int run, size, amp; - int c; - int i, j; - - if ((size = readHuffSym(dcHuffTable)) == 9999) { - return gFalse; - } - if (size > 0) { - if ((amp = readAmp(size)) == 9999) { - return gFalse; - } - } else { - amp = 0; - } - data[0] = *prevDC += amp; - for (i = 1; i < 64; ++i) { - data[i] = 0; - } - i = 1; - while (i < 64) { - run = 0; - while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) { - run += 0x10; - } - if (c == 9999) { - return gFalse; - } - if (c == 0x00) { - break; - } else { - run += (c >> 4) & 0x0f; - size = c & 0x0f; - amp = readAmp(size); - if (amp == 9999) { - return gFalse; - } - i += run; - if (i < 64) { - j = dctZigZag[i++]; - data[j] = amp; - } - } - } - return gTrue; -} - -// Read one data unit from a progressive JPEG stream. -GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable, - DCTHuffTable *acHuffTable, - int *prevDC, int data[64]) { - int run, size, amp, bit, c; - int i, j, k; - - // get the DC coefficient - i = scanInfo.firstCoeff; - if (i == 0) { - if (scanInfo.ah == 0) { - if ((size = readHuffSym(dcHuffTable)) == 9999) { - return gFalse; - } - if (size > 0) { - if ((amp = readAmp(size)) == 9999) { - return gFalse; - } - } else { - amp = 0; - } - data[0] += (*prevDC += amp) << scanInfo.al; - } else { - if ((bit = readBit()) == 9999) { - return gFalse; - } - if (bit) { - data[0] += 1 << scanInfo.al; - } - } - ++i; - } - if (scanInfo.lastCoeff == 0) { - return gTrue; - } - - // check for an EOB run - if (eobRun > 0) { - while (i <= scanInfo.lastCoeff) { - j = dctZigZag[i++]; - if (data[j] != 0) { - if ((bit = readBit()) == EOF) { - return gFalse; - } - if (bit) { - if (data[j] >= 0) { - data[j] += 1 << scanInfo.al; - } else { - data[j] -= 1 << scanInfo.al; - } - } - } - } - --eobRun; - return gTrue; - } - - // read the AC coefficients - while (i <= scanInfo.lastCoeff) { - if ((c = readHuffSym(acHuffTable)) == 9999) { - return gFalse; - } - - // ZRL - if (c == 0xf0) { - k = 0; - while (k < 16 && i <= scanInfo.lastCoeff) { - j = dctZigZag[i++]; - if (data[j] == 0) { - ++k; - } else { - if ((bit = readBit()) == EOF) { - return gFalse; - } - if (bit) { - if (data[j] >= 0) { - data[j] += 1 << scanInfo.al; - } else { - data[j] -= 1 << scanInfo.al; - } - } - } - } - - // EOB run - } else if ((c & 0x0f) == 0x00) { - j = c >> 4; - eobRun = 0; - for (k = 0; k < j; ++k) { - if ((bit = readBit()) == EOF) { - return gFalse; - } - eobRun = (eobRun << 1) | bit; - } - eobRun += 1 << j; - while (i <= scanInfo.lastCoeff) { - j = dctZigZag[i++]; - if (data[j] != 0) { - if ((bit = readBit()) == EOF) { - return gFalse; - } - if (bit) { - if (data[j] >= 0) { - data[j] += 1 << scanInfo.al; - } else { - data[j] -= 1 << scanInfo.al; - } - } - } - } - --eobRun; - break; - - // zero run and one AC coefficient - } else { - run = (c >> 4) & 0x0f; - size = c & 0x0f; - if ((amp = readAmp(size)) == 9999) { - return gFalse; - } - j = 0; // make gcc happy - for (k = 0; k <= run && i <= scanInfo.lastCoeff; ++k) { - j = dctZigZag[i++]; - while (data[j] != 0 && i <= scanInfo.lastCoeff) { - if ((bit = readBit()) == EOF) { - return gFalse; - } - if (bit) { - if (data[j] >= 0) { - data[j] += 1 << scanInfo.al; - } else { - data[j] -= 1 << scanInfo.al; - } - } - j = dctZigZag[i++]; - } - } - data[j] = amp << scanInfo.al; - } - } - - return gTrue; -} - -// Decode a progressive JPEG image. -void DCTStream::decodeImage() { - int dataIn[64]; - Guchar dataOut[64]; - Gushort *quantTable; - int pY, pCb, pCr, pR, pG, pB; - int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; - int h, v, horiz, vert, hSub, vSub; - int *p0, *p1, *p2; - - for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) { - for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) { - for (cc = 0; cc < numComps; ++cc) { - quantTable = quantTables[compInfo[cc].quantTable]; - h = compInfo[cc].hSample; - v = compInfo[cc].vSample; - horiz = mcuWidth / h; - vert = mcuHeight / v; - hSub = horiz / 8; - vSub = vert / 8; - for (y2 = 0; y2 < mcuHeight; y2 += vert) { - for (x2 = 0; x2 < mcuWidth; x2 += horiz) { - - // pull out the coded data unit - p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; - for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { - dataIn[i] = p1[0]; - dataIn[i+1] = p1[1]; - dataIn[i+2] = p1[2]; - dataIn[i+3] = p1[3]; - dataIn[i+4] = p1[4]; - dataIn[i+5] = p1[5]; - dataIn[i+6] = p1[6]; - dataIn[i+7] = p1[7]; - p1 += bufWidth * vSub; - } - - // transform - transformDataUnit(quantTable, dataIn, dataOut); - - // store back into frameBuf, doing replication for - // subsampled components - p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; - if (hSub == 1 && vSub == 1) { - for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { - p1[0] = dataOut[i] & 0xff; - p1[1] = dataOut[i+1] & 0xff; - p1[2] = dataOut[i+2] & 0xff; - p1[3] = dataOut[i+3] & 0xff; - p1[4] = dataOut[i+4] & 0xff; - p1[5] = dataOut[i+5] & 0xff; - p1[6] = dataOut[i+6] & 0xff; - p1[7] = dataOut[i+7] & 0xff; - p1 += bufWidth; - } - } else if (hSub == 2 && vSub == 2) { - p2 = p1 + bufWidth; - for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { - p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff; - p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i+1] & 0xff; - p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i+2] & 0xff; - p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i+3] & 0xff; - p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i+4] & 0xff; - p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i+5] & 0xff; - p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i+6] & 0xff; - p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i+7] & 0xff; - p1 += bufWidth * 2; - p2 += bufWidth * 2; - } - } else { - i = 0; - for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { - for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { - p2 = p1 + x4; - for (y5 = 0; y5 < vSub; ++y5) { - for (x5 = 0; x5 < hSub; ++x5) { - p2[x5] = dataOut[i] & 0xff; - } - p2 += bufWidth; - } - ++i; - } - p1 += bufWidth * vSub; - } - } - } - } - } - - // color space conversion - if (colorXform) { - // convert YCbCr to RGB - if (numComps == 3) { - for (y2 = 0; y2 < mcuHeight; ++y2) { - p0 = &frameBuf[0][(y1+y2) * bufWidth + x1]; - p1 = &frameBuf[1][(y1+y2) * bufWidth + x1]; - p2 = &frameBuf[2][(y1+y2) * bufWidth + x1]; - for (x2 = 0; x2 < mcuWidth; ++x2) { - pY = *p0; - pCb = *p1 - 128; - pCr = *p2 - 128; - pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; - *p0++ = dctClip(pR); - pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + - 32768) >> 16; - *p1++ = dctClip(pG); - pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; - *p2++ = dctClip(pB); - } - } - // convert YCbCrK to CMYK (K is passed through unchanged) - } else if (numComps == 4) { - for (y2 = 0; y2 < mcuHeight; ++y2) { - p0 = &frameBuf[0][(y1+y2) * bufWidth + x1]; - p1 = &frameBuf[1][(y1+y2) * bufWidth + x1]; - p2 = &frameBuf[2][(y1+y2) * bufWidth + x1]; - for (x2 = 0; x2 < mcuWidth; ++x2) { - pY = *p0; - pCb = *p1 - 128; - pCr = *p2 - 128; - pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; - *p0++ = 255 - dctClip(pR); - pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + - 32768) >> 16; - *p1++ = 255 - dctClip(pG); - pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; - *p2++ = 255 - dctClip(pB); - } - } - } - } - } - } -} - -// Transform one data unit -- this performs the dequantization and -// IDCT steps. This IDCT algorithm is taken from: -// Y. A. Reznik, A. T. Hinds, L. Yu, Z. Ni, and C-X. Zhang, -// "Efficient fixed-point approximations of the 8x8 inverse discrete -// cosine transform" (invited paper), Proc. SPIE Vol. 6696, Sep. 24, -// 2007. -// which is based on: -// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, -// "Practical Fast 1-D DCT Algorithms with 11 Multiplications", -// IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, -// 988-991. -// The stage numbers mentioned in the comments refer to Figure 1 in the -// Loeffler paper. -void DCTStream::transformDataUnit(Gushort *quantTable, - int dataIn[64], Guchar dataOut[64]) { - int v0, v1, v2, v3, v4, v5, v6, v7; - int t0, t1, t2, t3, t4, t5, t6, t7; - int *p, *scale; - Gushort *q; - int i; - - // dequant; inverse DCT on rows - for (i = 0; i < 64; i += 8) { - p = dataIn + i; - q = quantTable + i; - scale = idctScaleMat + i; - - // check for all-zero AC coefficients - if (p[1] == 0 && p[2] == 0 && p[3] == 0 && - p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) { - t0 = p[0] * q[0] * scale[0]; - if (i == 0) { - t0 += 1 << 12; // rounding bias - } - p[0] = t0; - p[1] = t0; - p[2] = t0; - p[3] = t0; - p[4] = t0; - p[5] = t0; - p[6] = t0; - p[7] = t0; - continue; - } - - // stage 4 - v0 = p[0] * q[0] * scale[0]; - if (i == 0) { - v0 += 1 << 12; // rounding bias - } - v1 = p[4] * q[4] * scale[4]; - v2 = p[2] * q[2] * scale[2]; - v3 = p[6] * q[6] * scale[6]; - t0 = p[1] * q[1] * scale[1]; - t1 = p[7] * q[7] * scale[7]; - v4 = t0 - t1; - v7 = t0 + t1; - v5 = p[3] * q[3] * scale[3]; - v6 = p[5] * q[5] * scale[5]; - - // stage 3 - t0 = v0 - v1; - v0 = v0 + v1; - v1 = t0; - t0 = v2 + (v2 >> 5); - t1 = t0 >> 2; - t2 = t1 + (v2 >> 4); // 41/128 * v2 - t3 = t0 - t1; // 99/128 * v2 - t4 = v3 + (v3 >> 5); - t5 = t4 >> 2; - t6 = t5 + (v3 >> 4); // 41/128 * v3 - t7 = t4 - t5; // 99/128 * v3 - v2 = t2 - t7; - v3 = t3 + t6; - t0 = v4 - v6; - v4 = v4 + v6; - v6 = t0; - t0 = v7 + v5; - v5 = v7 - v5; - v7 = t0; - - // stage 2 - t0 = v0 - v3; - v0 = v0 + v3; - v3 = t0; - t0 = v1 - v2; - v1 = v1 + v2; - v2 = t0; - t0 = (v4 >> 9) - v4; - t1 = v4 >> 1; // 1/2 * v4 - t2 = (t0 >> 2) - t0; // 1533/2048 * v4 - t3 = (v7 >> 9) - v7; - t4 = v7 >> 1; // 1/2 * v7 - t5 = (t3 >> 2) - t3; // 1533/2048 * v7 - v4 = t2 - t4; - v7 = t1 + t5; - t0 = (v5 >> 3) - (v5 >> 7); - t1 = t0 - (v5 >> 11); - t2 = t0 + (t1 >> 1); // 719/4096 * v5 - t3 = v5 - t0; // 113/256 * v5 - t4 = (v6 >> 3) - (v6 >> 7); - t5 = t4 - (v6 >> 11); - t6 = t4 + (t5 >> 1); // 719/4096 * v6 - t7 = v6 - t4; // 113/256 * v6 - v5 = t3 - t6; - v6 = t2 + t7; - - // stage 1 - p[0] = v0 + v7; - p[7] = v0 - v7; - p[1] = v1 + v6; - p[6] = v1 - v6; - p[2] = v2 + v5; - p[5] = v2 - v5; - p[3] = v3 + v4; - p[4] = v3 - v4; - } - - // inverse DCT on columns - for (i = 0; i < 8; ++i) { - p = dataIn + i; - - // check for all-zero AC coefficients - if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 && - p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) { - t0 = p[0*8]; - p[1*8] = t0; - p[2*8] = t0; - p[3*8] = t0; - p[4*8] = t0; - p[5*8] = t0; - p[6*8] = t0; - p[7*8] = t0; - continue; - } - - // stage 4 - v0 = p[0*8]; - v1 = p[4*8]; - v2 = p[2*8]; - v3 = p[6*8]; - t0 = p[1*8]; - t1 = p[7*8]; - v4 = t0 - t1; - v7 = t0 + t1; - v5 = p[3*8]; - v6 = p[5*8]; - - // stage 3 - t0 = v0 - v1; - v0 = v0 + v1; - v1 = t0; - t0 = v2 + (v2 >> 5); - t1 = t0 >> 2; - t2 = t1 + (v2 >> 4); // 41/128 * v2 - t3 = t0 - t1; // 99/128 * v2 - t4 = v3 + (v3 >> 5); - t5 = t4 >> 2; - t6 = t5 + (v3 >> 4); // 41/128 * v3 - t7 = t4 - t5; // 99/128 * v3 - v2 = t2 - t7; - v3 = t3 + t6; - t0 = v4 - v6; - v4 = v4 + v6; - v6 = t0; - t0 = v7 + v5; - v5 = v7 - v5; - v7 = t0; - - // stage 2 - t0 = v0 - v3; - v0 = v0 + v3; - v3 = t0; - t0 = v1 - v2; - v1 = v1 + v2; - v2 = t0; - t0 = (v4 >> 9) - v4; - t1 = v4 >> 1; // 1/2 * v4 - t2 = (t0 >> 2) - t0; // 1533/2048 * v4 - t3 = (v7 >> 9) - v7; - t4 = v7 >> 1; // 1/2 * v7 - t5 = (t3 >> 2) - t3; // 1533/2048 * v7 - v4 = t2 - t4; - v7 = t1 + t5; - t0 = (v5 >> 3) - (v5 >> 7); - t1 = t0 - (v5 >> 11); - t2 = t0 + (t1 >> 1); // 719/4096 * v5 - t3 = v5 - t0; // 113/256 * v5 - t4 = (v6 >> 3) - (v6 >> 7); - t5 = t4 - (v6 >> 11); - t6 = t4 + (t5 >> 1); // 719/4096 * v6 - t7 = v6 - t4; // 113/256 * v6 - v5 = t3 - t6; - v6 = t2 + t7; - - // stage 1 - p[0*8] = v0 + v7; - p[7*8] = v0 - v7; - p[1*8] = v1 + v6; - p[6*8] = v1 - v6; - p[2*8] = v2 + v5; - p[5*8] = v2 - v5; - p[3*8] = v3 + v4; - p[4*8] = v3 - v4; - } - - // convert to 8-bit integers - for (i = 0; i < 64; ++i) { - dataOut[i] = dctClip(128 + (dataIn[i] >> 13)); - } -} - -int DCTStream::readHuffSym(DCTHuffTable *table) { - Gushort code; - int bit; - int codeBits; - - code = 0; - codeBits = 0; - do { - // add a bit to the code - if ((bit = readBit()) == EOF) { - return 9999; - } - code = (Gushort)((code << 1) + bit); - ++codeBits; - - // look up code - if (code < table->firstCode[codeBits]) { - break; - } - if (code - table->firstCode[codeBits] < table->numCodes[codeBits]) { - code = (Gushort)(code - table->firstCode[codeBits]); - return table->sym[table->firstSym[codeBits] + code]; - } - } while (codeBits < 16); - - error(errSyntaxError, getPos(), "Bad Huffman code in DCT stream"); - return 9999; -} - -int DCTStream::readAmp(int size) { - int amp, bit; - int bits; - - amp = 0; - for (bits = 0; bits < size; ++bits) { - if ((bit = readBit()) == EOF) - return 9999; - amp = (amp << 1) + bit; - } - if (amp < (1 << (size - 1))) - amp -= (1 << size) - 1; - return amp; -} - -int DCTStream::readBit() { - int bit; - int c, c2; - - if (inputBits == 0) { - if ((c = str->getChar()) == EOF) - return EOF; - if (c == 0xff) { - do { - c2 = str->getChar(); - } while (c2 == 0xff); - if (c2 != 0x00) { - error(errSyntaxError, getPos(), "Bad DCT data: missing 00 after ff"); - return EOF; - } - } - inputBuf = c; - inputBits = 8; - } - bit = (inputBuf >> (inputBits - 1)) & 1; - --inputBits; - return bit; -} - -GBool DCTStream::readHeader(GBool frame) { - GBool doScan; - int n; - int c = 0; - - // read headers - doScan = gFalse; - while (!doScan) { - c = readMarker(); - switch (c) { - case 0xc0: // SOF0 (sequential) - case 0xc1: // SOF1 (extended sequential) - if (!frame) { - error(errSyntaxError, getPos(), - "Invalid DCT marker in scan <{0:02x}>", c); - return gFalse; - } - if (!readBaselineSOF()) { - return gFalse; - } - break; - case 0xc2: // SOF2 (progressive) - if (!frame) { - error(errSyntaxError, getPos(), - "Invalid DCT marker in scan <{0:02x}>", c); - return gFalse; - } - if (!readProgressiveSOF()) { - return gFalse; - } - break; - case 0xc4: // DHT - if (!readHuffmanTables()) { - return gFalse; - } - break; - case 0xd8: // SOI - if (!frame) { - error(errSyntaxError, getPos(), - "Invalid DCT marker in scan <{0:02x}>", c); - return gFalse; - } - break; - case 0xd9: // EOI - return gFalse; - case 0xda: // SOS - if (!readScanInfo()) { - return gFalse; - } - doScan = gTrue; - break; - case 0xdb: // DQT - if (!readQuantTables()) { - return gFalse; - } - break; - case 0xdd: // DRI - if (!readRestartInterval()) { - return gFalse; - } - break; - case 0xe0: // APP0 - if (!frame) { - error(errSyntaxError, getPos(), - "Invalid DCT marker in scan <{0:02x}>", c); - return gFalse; - } - if (!readJFIFMarker()) { - return gFalse; - } - break; - case 0xee: // APP14 - if (!frame) { - error(errSyntaxError, getPos(), - "Invalid DCT marker in scan <{0:02x}>", c); - return gFalse; - } - if (!readAdobeMarker()) { - return gFalse; - } - break; - case EOF: - error(errSyntaxError, getPos(), "Bad DCT header"); - return gFalse; - default: - // skip APPn / COM / etc. - if (c >= 0xe0) { - n = read16() - 2; - str->discardChars(n); - } else { - error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c); - return gFalse; - } - break; - } - } - - return gTrue; -} - -GBool DCTStream::readBaselineSOF() { - int prec; - int i; - int c; - - read16(); // length - prec = str->getChar(); - height = read16(); - width = read16(); - numComps = str->getChar(); - if (numComps <= 0 || numComps > 4) { - error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); - numComps = 0; - return gFalse; - } - if (prec != 8) { - error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); - return gFalse; - } - for (i = 0; i < numComps; ++i) { - compInfo[i].id = str->getChar(); - c = str->getChar(); - compInfo[i].hSample = (c >> 4) & 0x0f; - compInfo[i].vSample = c & 0x0f; - compInfo[i].quantTable = str->getChar(); - if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || - compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { - error(errSyntaxError, getPos(), "Bad DCT sampling factor"); - return gFalse; - } - if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { - error(errSyntaxError, getPos(), "Bad DCT quant table selector"); - return gFalse; - } - } - progressive = gFalse; - return gTrue; -} - -GBool DCTStream::readProgressiveSOF() { - int prec; - int i; - int c; - - read16(); // length - prec = str->getChar(); - height = read16(); - width = read16(); - numComps = str->getChar(); - if (numComps <= 0 || numComps > 4) { - error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); - numComps = 0; - return gFalse; - } - if (prec != 8) { - error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); - return gFalse; - } - for (i = 0; i < numComps; ++i) { - compInfo[i].id = str->getChar(); - c = str->getChar(); - compInfo[i].hSample = (c >> 4) & 0x0f; - compInfo[i].vSample = c & 0x0f; - compInfo[i].quantTable = str->getChar(); - if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || - compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { - error(errSyntaxError, getPos(), "Bad DCT sampling factor"); - return gFalse; - } - if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { - error(errSyntaxError, getPos(), "Bad DCT quant table selector"); - return gFalse; - } - } - progressive = gTrue; - return gTrue; -} - -GBool DCTStream::readScanInfo() { - int length; - int id, c; - int i, j; - - length = read16() - 2; - scanInfo.numComps = str->getChar(); - if (scanInfo.numComps <= 0 || scanInfo.numComps > 4) { - error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); - scanInfo.numComps = 0; - return gFalse; - } - --length; - if (length != 2 * scanInfo.numComps + 3) { - error(errSyntaxError, getPos(), "Bad DCT scan info block"); - return gFalse; - } - interleaved = scanInfo.numComps == numComps; - for (j = 0; j < numComps; ++j) { - scanInfo.comp[j] = gFalse; - } - for (i = 0; i < scanInfo.numComps; ++i) { - id = str->getChar(); - // some (broken) DCT streams reuse ID numbers, but at least they - // keep the components in order, so we check compInfo[i] first to - // work around the problem - if (id == compInfo[i].id) { - j = i; - } else { - for (j = 0; j < numComps; ++j) { - if (id == compInfo[j].id) { - break; - } - } - if (j == numComps) { - error(errSyntaxError, getPos(), - "Bad DCT component ID in scan info block"); - return gFalse; - } - } - if (scanInfo.comp[j]) { - error(errSyntaxError, getPos(), - "Invalid DCT component ID in scan info block"); - return gFalse; - } - scanInfo.comp[j] = gTrue; - c = str->getChar(); - scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f; - scanInfo.acHuffTable[j] = c & 0x0f; - } - scanInfo.firstCoeff = str->getChar(); - scanInfo.lastCoeff = str->getChar(); - if (scanInfo.firstCoeff < 0 || scanInfo.lastCoeff > 63 || - scanInfo.firstCoeff > scanInfo.lastCoeff) { - error(errSyntaxError, getPos(), - "Bad DCT coefficient numbers in scan info block"); - return gFalse; - } - c = str->getChar(); - scanInfo.ah = (c >> 4) & 0x0f; - scanInfo.al = c & 0x0f; - return gTrue; -} - -GBool DCTStream::readQuantTables() { - int length, prec, i, index; - - length = read16() - 2; - while (length > 0) { - index = str->getChar(); - prec = (index >> 4) & 0x0f; - index &= 0x0f; - if (prec > 1 || index >= 4) { - error(errSyntaxError, getPos(), "Bad DCT quantization table"); - return gFalse; - } - if (index == numQuantTables) { - numQuantTables = index + 1; - } - for (i = 0; i < 64; ++i) { - if (prec) { - quantTables[index][dctZigZag[i]] = (Gushort)read16(); - } else { - quantTables[index][dctZigZag[i]] = (Gushort)str->getChar(); - } - } - if (prec) { - length -= 129; - } else { - length -= 65; - } - } - return gTrue; -} - -GBool DCTStream::readHuffmanTables() { - DCTHuffTable *tbl; - int length; - int index; - Gushort code; - Guchar sym; - int i; - int c; - - length = read16() - 2; - while (length > 0) { - index = str->getChar(); - --length; - if ((index & 0x0f) >= 4) { - error(errSyntaxError, getPos(), "Bad DCT Huffman table"); - return gFalse; - } - if (index & 0x10) { - index &= 0x0f; - if (index >= numACHuffTables) - numACHuffTables = index+1; - tbl = &acHuffTables[index]; - } else { - index &= 0x0f; - if (index >= numDCHuffTables) - numDCHuffTables = index+1; - tbl = &dcHuffTables[index]; - } - sym = 0; - code = 0; - for (i = 1; i <= 16; ++i) { - c = str->getChar(); - tbl->firstSym[i] = sym; - tbl->firstCode[i] = code; - tbl->numCodes[i] = (Gushort)c; - sym = (Guchar)(sym + c); - code = (Gushort)((code + c) << 1); - } - length -= 16; - for (i = 0; i < sym; ++i) - tbl->sym[i] = (Guchar)str->getChar(); - length -= sym; - } - return gTrue; -} - -GBool DCTStream::readRestartInterval() { - int length; - - length = read16(); - if (length != 4) { - error(errSyntaxError, getPos(), "Bad DCT restart interval"); - return gFalse; - } - restartInterval = read16(); - return gTrue; -} - -GBool DCTStream::readJFIFMarker() { - int length, i; - char buf[5]; - int c; - - length = read16(); - length -= 2; - if (length >= 5) { - for (i = 0; i < 5; ++i) { - if ((c = str->getChar()) == EOF) { - error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); - return gFalse; - } - buf[i] = (char)c; - } - length -= 5; - if (!memcmp(buf, "JFIF\0", 5)) { - gotJFIFMarker = gTrue; - } - } - while (length > 0) { - if (str->getChar() == EOF) { - error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); - return gFalse; - } - --length; - } - return gTrue; -} - -GBool DCTStream::readAdobeMarker() { - int length, i; - char buf[12]; - int c; - - length = read16(); - if (length < 14) { - goto err; - } - for (i = 0; i < 12; ++i) { - if ((c = str->getChar()) == EOF) { - goto err; - } - buf[i] = (char)c; - } - if (!strncmp(buf, "Adobe", 5)) { - colorXform = buf[11]; - gotAdobeMarker = gTrue; - } - for (i = 14; i < length; ++i) { - if (str->getChar() == EOF) { - goto err; - } - } - return gTrue; - - err: - error(errSyntaxError, getPos(), "Bad DCT Adobe APP14 marker"); - return gFalse; -} - -GBool DCTStream::readTrailer() { - int c; - - c = readMarker(); - if (c != 0xd9) { // EOI - error(errSyntaxError, getPos(), "Bad DCT trailer"); - return gFalse; - } - return gTrue; -} - -int DCTStream::readMarker() { - int c; - - do { - do { - c = str->getChar(); - } while (c != 0xff && c != EOF); - do { - c = str->getChar(); - } while (c == 0xff); - } while (c == 0x00); - return c; -} - -int DCTStream::read16() { - int c1, c2; - - if ((c1 = str->getChar()) == EOF) - return EOF; - if ((c2 = str->getChar()) == EOF) - return EOF; - return (c1 << 8) + c2; -} - -#endif // HAVE_JPEGLIB - -GString *DCTStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 2) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("<< >> /DCTDecode filter\n"); - return s; -} - -GBool DCTStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -//------------------------------------------------------------------------ -// FlateStream -//------------------------------------------------------------------------ - -int FlateStream::codeLenCodeMap[flateMaxCodeLenCodes] = { - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 -}; - -FlateDecode FlateStream::lengthDecode[flateMaxLitCodes-257] = { - {0, 3}, - {0, 4}, - {0, 5}, - {0, 6}, - {0, 7}, - {0, 8}, - {0, 9}, - {0, 10}, - {1, 11}, - {1, 13}, - {1, 15}, - {1, 17}, - {2, 19}, - {2, 23}, - {2, 27}, - {2, 31}, - {3, 35}, - {3, 43}, - {3, 51}, - {3, 59}, - {4, 67}, - {4, 83}, - {4, 99}, - {4, 115}, - {5, 131}, - {5, 163}, - {5, 195}, - {5, 227}, - {0, 258}, - {0, 258}, - {0, 258} -}; - -FlateDecode FlateStream::distDecode[flateMaxDistCodes] = { - { 0, 1}, - { 0, 2}, - { 0, 3}, - { 0, 4}, - { 1, 5}, - { 1, 7}, - { 2, 9}, - { 2, 13}, - { 3, 17}, - { 3, 25}, - { 4, 33}, - { 4, 49}, - { 5, 65}, - { 5, 97}, - { 6, 129}, - { 6, 193}, - { 7, 257}, - { 7, 385}, - { 8, 513}, - { 8, 769}, - { 9, 1025}, - { 9, 1537}, - {10, 2049}, - {10, 3073}, - {11, 4097}, - {11, 6145}, - {12, 8193}, - {12, 12289}, - {13, 16385}, - {13, 24577} -}; - -static FlateCode flateFixedLitCodeTabCodes[512] = { - {7, 0x0100}, - {8, 0x0050}, - {8, 0x0010}, - {8, 0x0118}, - {7, 0x0110}, - {8, 0x0070}, - {8, 0x0030}, - {9, 0x00c0}, - {7, 0x0108}, - {8, 0x0060}, - {8, 0x0020}, - {9, 0x00a0}, - {8, 0x0000}, - {8, 0x0080}, - {8, 0x0040}, - {9, 0x00e0}, - {7, 0x0104}, - {8, 0x0058}, - {8, 0x0018}, - {9, 0x0090}, - {7, 0x0114}, - {8, 0x0078}, - {8, 0x0038}, - {9, 0x00d0}, - {7, 0x010c}, - {8, 0x0068}, - {8, 0x0028}, - {9, 0x00b0}, - {8, 0x0008}, - {8, 0x0088}, - {8, 0x0048}, - {9, 0x00f0}, - {7, 0x0102}, - {8, 0x0054}, - {8, 0x0014}, - {8, 0x011c}, - {7, 0x0112}, - {8, 0x0074}, - {8, 0x0034}, - {9, 0x00c8}, - {7, 0x010a}, - {8, 0x0064}, - {8, 0x0024}, - {9, 0x00a8}, - {8, 0x0004}, - {8, 0x0084}, - {8, 0x0044}, - {9, 0x00e8}, - {7, 0x0106}, - {8, 0x005c}, - {8, 0x001c}, - {9, 0x0098}, - {7, 0x0116}, - {8, 0x007c}, - {8, 0x003c}, - {9, 0x00d8}, - {7, 0x010e}, - {8, 0x006c}, - {8, 0x002c}, - {9, 0x00b8}, - {8, 0x000c}, - {8, 0x008c}, - {8, 0x004c}, - {9, 0x00f8}, - {7, 0x0101}, - {8, 0x0052}, - {8, 0x0012}, - {8, 0x011a}, - {7, 0x0111}, - {8, 0x0072}, - {8, 0x0032}, - {9, 0x00c4}, - {7, 0x0109}, - {8, 0x0062}, - {8, 0x0022}, - {9, 0x00a4}, - {8, 0x0002}, - {8, 0x0082}, - {8, 0x0042}, - {9, 0x00e4}, - {7, 0x0105}, - {8, 0x005a}, - {8, 0x001a}, - {9, 0x0094}, - {7, 0x0115}, - {8, 0x007a}, - {8, 0x003a}, - {9, 0x00d4}, - {7, 0x010d}, - {8, 0x006a}, - {8, 0x002a}, - {9, 0x00b4}, - {8, 0x000a}, - {8, 0x008a}, - {8, 0x004a}, - {9, 0x00f4}, - {7, 0x0103}, - {8, 0x0056}, - {8, 0x0016}, - {8, 0x011e}, - {7, 0x0113}, - {8, 0x0076}, - {8, 0x0036}, - {9, 0x00cc}, - {7, 0x010b}, - {8, 0x0066}, - {8, 0x0026}, - {9, 0x00ac}, - {8, 0x0006}, - {8, 0x0086}, - {8, 0x0046}, - {9, 0x00ec}, - {7, 0x0107}, - {8, 0x005e}, - {8, 0x001e}, - {9, 0x009c}, - {7, 0x0117}, - {8, 0x007e}, - {8, 0x003e}, - {9, 0x00dc}, - {7, 0x010f}, - {8, 0x006e}, - {8, 0x002e}, - {9, 0x00bc}, - {8, 0x000e}, - {8, 0x008e}, - {8, 0x004e}, - {9, 0x00fc}, - {7, 0x0100}, - {8, 0x0051}, - {8, 0x0011}, - {8, 0x0119}, - {7, 0x0110}, - {8, 0x0071}, - {8, 0x0031}, - {9, 0x00c2}, - {7, 0x0108}, - {8, 0x0061}, - {8, 0x0021}, - {9, 0x00a2}, - {8, 0x0001}, - {8, 0x0081}, - {8, 0x0041}, - {9, 0x00e2}, - {7, 0x0104}, - {8, 0x0059}, - {8, 0x0019}, - {9, 0x0092}, - {7, 0x0114}, - {8, 0x0079}, - {8, 0x0039}, - {9, 0x00d2}, - {7, 0x010c}, - {8, 0x0069}, - {8, 0x0029}, - {9, 0x00b2}, - {8, 0x0009}, - {8, 0x0089}, - {8, 0x0049}, - {9, 0x00f2}, - {7, 0x0102}, - {8, 0x0055}, - {8, 0x0015}, - {8, 0x011d}, - {7, 0x0112}, - {8, 0x0075}, - {8, 0x0035}, - {9, 0x00ca}, - {7, 0x010a}, - {8, 0x0065}, - {8, 0x0025}, - {9, 0x00aa}, - {8, 0x0005}, - {8, 0x0085}, - {8, 0x0045}, - {9, 0x00ea}, - {7, 0x0106}, - {8, 0x005d}, - {8, 0x001d}, - {9, 0x009a}, - {7, 0x0116}, - {8, 0x007d}, - {8, 0x003d}, - {9, 0x00da}, - {7, 0x010e}, - {8, 0x006d}, - {8, 0x002d}, - {9, 0x00ba}, - {8, 0x000d}, - {8, 0x008d}, - {8, 0x004d}, - {9, 0x00fa}, - {7, 0x0101}, - {8, 0x0053}, - {8, 0x0013}, - {8, 0x011b}, - {7, 0x0111}, - {8, 0x0073}, - {8, 0x0033}, - {9, 0x00c6}, - {7, 0x0109}, - {8, 0x0063}, - {8, 0x0023}, - {9, 0x00a6}, - {8, 0x0003}, - {8, 0x0083}, - {8, 0x0043}, - {9, 0x00e6}, - {7, 0x0105}, - {8, 0x005b}, - {8, 0x001b}, - {9, 0x0096}, - {7, 0x0115}, - {8, 0x007b}, - {8, 0x003b}, - {9, 0x00d6}, - {7, 0x010d}, - {8, 0x006b}, - {8, 0x002b}, - {9, 0x00b6}, - {8, 0x000b}, - {8, 0x008b}, - {8, 0x004b}, - {9, 0x00f6}, - {7, 0x0103}, - {8, 0x0057}, - {8, 0x0017}, - {8, 0x011f}, - {7, 0x0113}, - {8, 0x0077}, - {8, 0x0037}, - {9, 0x00ce}, - {7, 0x010b}, - {8, 0x0067}, - {8, 0x0027}, - {9, 0x00ae}, - {8, 0x0007}, - {8, 0x0087}, - {8, 0x0047}, - {9, 0x00ee}, - {7, 0x0107}, - {8, 0x005f}, - {8, 0x001f}, - {9, 0x009e}, - {7, 0x0117}, - {8, 0x007f}, - {8, 0x003f}, - {9, 0x00de}, - {7, 0x010f}, - {8, 0x006f}, - {8, 0x002f}, - {9, 0x00be}, - {8, 0x000f}, - {8, 0x008f}, - {8, 0x004f}, - {9, 0x00fe}, - {7, 0x0100}, - {8, 0x0050}, - {8, 0x0010}, - {8, 0x0118}, - {7, 0x0110}, - {8, 0x0070}, - {8, 0x0030}, - {9, 0x00c1}, - {7, 0x0108}, - {8, 0x0060}, - {8, 0x0020}, - {9, 0x00a1}, - {8, 0x0000}, - {8, 0x0080}, - {8, 0x0040}, - {9, 0x00e1}, - {7, 0x0104}, - {8, 0x0058}, - {8, 0x0018}, - {9, 0x0091}, - {7, 0x0114}, - {8, 0x0078}, - {8, 0x0038}, - {9, 0x00d1}, - {7, 0x010c}, - {8, 0x0068}, - {8, 0x0028}, - {9, 0x00b1}, - {8, 0x0008}, - {8, 0x0088}, - {8, 0x0048}, - {9, 0x00f1}, - {7, 0x0102}, - {8, 0x0054}, - {8, 0x0014}, - {8, 0x011c}, - {7, 0x0112}, - {8, 0x0074}, - {8, 0x0034}, - {9, 0x00c9}, - {7, 0x010a}, - {8, 0x0064}, - {8, 0x0024}, - {9, 0x00a9}, - {8, 0x0004}, - {8, 0x0084}, - {8, 0x0044}, - {9, 0x00e9}, - {7, 0x0106}, - {8, 0x005c}, - {8, 0x001c}, - {9, 0x0099}, - {7, 0x0116}, - {8, 0x007c}, - {8, 0x003c}, - {9, 0x00d9}, - {7, 0x010e}, - {8, 0x006c}, - {8, 0x002c}, - {9, 0x00b9}, - {8, 0x000c}, - {8, 0x008c}, - {8, 0x004c}, - {9, 0x00f9}, - {7, 0x0101}, - {8, 0x0052}, - {8, 0x0012}, - {8, 0x011a}, - {7, 0x0111}, - {8, 0x0072}, - {8, 0x0032}, - {9, 0x00c5}, - {7, 0x0109}, - {8, 0x0062}, - {8, 0x0022}, - {9, 0x00a5}, - {8, 0x0002}, - {8, 0x0082}, - {8, 0x0042}, - {9, 0x00e5}, - {7, 0x0105}, - {8, 0x005a}, - {8, 0x001a}, - {9, 0x0095}, - {7, 0x0115}, - {8, 0x007a}, - {8, 0x003a}, - {9, 0x00d5}, - {7, 0x010d}, - {8, 0x006a}, - {8, 0x002a}, - {9, 0x00b5}, - {8, 0x000a}, - {8, 0x008a}, - {8, 0x004a}, - {9, 0x00f5}, - {7, 0x0103}, - {8, 0x0056}, - {8, 0x0016}, - {8, 0x011e}, - {7, 0x0113}, - {8, 0x0076}, - {8, 0x0036}, - {9, 0x00cd}, - {7, 0x010b}, - {8, 0x0066}, - {8, 0x0026}, - {9, 0x00ad}, - {8, 0x0006}, - {8, 0x0086}, - {8, 0x0046}, - {9, 0x00ed}, - {7, 0x0107}, - {8, 0x005e}, - {8, 0x001e}, - {9, 0x009d}, - {7, 0x0117}, - {8, 0x007e}, - {8, 0x003e}, - {9, 0x00dd}, - {7, 0x010f}, - {8, 0x006e}, - {8, 0x002e}, - {9, 0x00bd}, - {8, 0x000e}, - {8, 0x008e}, - {8, 0x004e}, - {9, 0x00fd}, - {7, 0x0100}, - {8, 0x0051}, - {8, 0x0011}, - {8, 0x0119}, - {7, 0x0110}, - {8, 0x0071}, - {8, 0x0031}, - {9, 0x00c3}, - {7, 0x0108}, - {8, 0x0061}, - {8, 0x0021}, - {9, 0x00a3}, - {8, 0x0001}, - {8, 0x0081}, - {8, 0x0041}, - {9, 0x00e3}, - {7, 0x0104}, - {8, 0x0059}, - {8, 0x0019}, - {9, 0x0093}, - {7, 0x0114}, - {8, 0x0079}, - {8, 0x0039}, - {9, 0x00d3}, - {7, 0x010c}, - {8, 0x0069}, - {8, 0x0029}, - {9, 0x00b3}, - {8, 0x0009}, - {8, 0x0089}, - {8, 0x0049}, - {9, 0x00f3}, - {7, 0x0102}, - {8, 0x0055}, - {8, 0x0015}, - {8, 0x011d}, - {7, 0x0112}, - {8, 0x0075}, - {8, 0x0035}, - {9, 0x00cb}, - {7, 0x010a}, - {8, 0x0065}, - {8, 0x0025}, - {9, 0x00ab}, - {8, 0x0005}, - {8, 0x0085}, - {8, 0x0045}, - {9, 0x00eb}, - {7, 0x0106}, - {8, 0x005d}, - {8, 0x001d}, - {9, 0x009b}, - {7, 0x0116}, - {8, 0x007d}, - {8, 0x003d}, - {9, 0x00db}, - {7, 0x010e}, - {8, 0x006d}, - {8, 0x002d}, - {9, 0x00bb}, - {8, 0x000d}, - {8, 0x008d}, - {8, 0x004d}, - {9, 0x00fb}, - {7, 0x0101}, - {8, 0x0053}, - {8, 0x0013}, - {8, 0x011b}, - {7, 0x0111}, - {8, 0x0073}, - {8, 0x0033}, - {9, 0x00c7}, - {7, 0x0109}, - {8, 0x0063}, - {8, 0x0023}, - {9, 0x00a7}, - {8, 0x0003}, - {8, 0x0083}, - {8, 0x0043}, - {9, 0x00e7}, - {7, 0x0105}, - {8, 0x005b}, - {8, 0x001b}, - {9, 0x0097}, - {7, 0x0115}, - {8, 0x007b}, - {8, 0x003b}, - {9, 0x00d7}, - {7, 0x010d}, - {8, 0x006b}, - {8, 0x002b}, - {9, 0x00b7}, - {8, 0x000b}, - {8, 0x008b}, - {8, 0x004b}, - {9, 0x00f7}, - {7, 0x0103}, - {8, 0x0057}, - {8, 0x0017}, - {8, 0x011f}, - {7, 0x0113}, - {8, 0x0077}, - {8, 0x0037}, - {9, 0x00cf}, - {7, 0x010b}, - {8, 0x0067}, - {8, 0x0027}, - {9, 0x00af}, - {8, 0x0007}, - {8, 0x0087}, - {8, 0x0047}, - {9, 0x00ef}, - {7, 0x0107}, - {8, 0x005f}, - {8, 0x001f}, - {9, 0x009f}, - {7, 0x0117}, - {8, 0x007f}, - {8, 0x003f}, - {9, 0x00df}, - {7, 0x010f}, - {8, 0x006f}, - {8, 0x002f}, - {9, 0x00bf}, - {8, 0x000f}, - {8, 0x008f}, - {8, 0x004f}, - {9, 0x00ff} -}; - -FlateHuffmanTab FlateStream::fixedLitCodeTab = { - flateFixedLitCodeTabCodes, 9 -}; - -static FlateCode flateFixedDistCodeTabCodes[32] = { - {5, 0x0000}, - {5, 0x0010}, - {5, 0x0008}, - {5, 0x0018}, - {5, 0x0004}, - {5, 0x0014}, - {5, 0x000c}, - {5, 0x001c}, - {5, 0x0002}, - {5, 0x0012}, - {5, 0x000a}, - {5, 0x001a}, - {5, 0x0006}, - {5, 0x0016}, - {5, 0x000e}, - {0, 0x0000}, - {5, 0x0001}, - {5, 0x0011}, - {5, 0x0009}, - {5, 0x0019}, - {5, 0x0005}, - {5, 0x0015}, - {5, 0x000d}, - {5, 0x001d}, - {5, 0x0003}, - {5, 0x0013}, - {5, 0x000b}, - {5, 0x001b}, - {5, 0x0007}, - {5, 0x0017}, - {5, 0x000f}, - {0, 0x0000} -}; - -FlateHuffmanTab FlateStream::fixedDistCodeTab = { - flateFixedDistCodeTabCodes, 5 -}; - -FlateStream::FlateStream(Stream *strA, int predictor, int columns, - int colors, int bits): - FilterStream(strA) { - if (predictor != 1) { - pred = new StreamPredictor(this, predictor, columns, colors, bits); - if (!pred->isOk()) { - delete pred; - pred = NULL; - } - } else { - pred = NULL; - } - litCodeTab.codes = NULL; - distCodeTab.codes = NULL; - memset(buf, 0, flateWindow); -} - -FlateStream::~FlateStream() { - if (litCodeTab.codes != fixedLitCodeTab.codes) { - gfree(litCodeTab.codes); - } - if (distCodeTab.codes != fixedDistCodeTab.codes) { - gfree(distCodeTab.codes); - } - if (pred) { - delete pred; - } - delete str; -} - -Stream *FlateStream::copy() { - if (pred) { - return new FlateStream(str->copy(), pred->getPredictor(), - pred->getWidth(), pred->getNComps(), - pred->getNBits()); - } else { - return new FlateStream(str->copy(), 1, 0, 0, 0); - } -} - -void FlateStream::reset() { - int cmf, flg; - - index = 0; - remain = 0; - codeBuf = 0; - codeSize = 0; - compressedBlock = gFalse; - endOfBlock = gTrue; - eof = gTrue; - - str->reset(); - if (pred) { - pred->reset(); - } - - // read header - //~ need to look at window size? - endOfBlock = eof = gTrue; - cmf = str->getChar(); - flg = str->getChar(); - if (cmf == EOF || flg == EOF) - return; - if ((cmf & 0x0f) != 0x08) { - error(errSyntaxError, getPos(), - "Unknown compression method in flate stream"); - return; - } - if ((((cmf << 8) + flg) % 31) != 0) { - error(errSyntaxError, getPos(), "Bad FCHECK in flate stream"); - return; - } - if (flg & 0x20) { - error(errSyntaxError, getPos(), "FDICT bit set in flate stream"); - return; - } - - eof = gFalse; -} - -int FlateStream::getChar() { - int c; - - if (pred) { - return pred->getChar(); - } - while (remain == 0) { - if (endOfBlock && eof) - return EOF; - readSome(); - } - c = buf[index]; - index = (index + 1) & flateMask; - --remain; - return c; -} - -int FlateStream::lookChar() { - int c; - - if (pred) { - return pred->lookChar(); - } - while (remain == 0) { - if (endOfBlock && eof) - return EOF; - readSome(); - } - c = buf[index]; - return c; -} - -int FlateStream::getRawChar() { - int c; - - while (remain == 0) { - if (endOfBlock && eof) - return EOF; - readSome(); - } - c = buf[index]; - index = (index + 1) & flateMask; - --remain; - return c; -} - -int FlateStream::getBlock(char *blk, int size) { - int n; - - if (pred) { - return pred->getBlock(blk, size); - } - - n = 0; - while (n < size) { - if (remain == 0) { - if (endOfBlock && eof) { - break; - } - readSome(); - } - while (remain && n < size) { - blk[n++] = buf[index]; - index = (index + 1) & flateMask; - --remain; - } - } - return n; -} - -GString *FlateStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 3 || pred) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("<< >> /FlateDecode filter\n"); - return s; -} - -GBool FlateStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -void FlateStream::readSome() { - int code1, code2; - int len, dist; - int i, j, k; - int c; - - if (endOfBlock) { - if (!startBlock()) - return; - } - - if (compressedBlock) { - if ((code1 = getHuffmanCodeWord(&litCodeTab)) == EOF) - goto err; - if (code1 < 256) { - buf[index] = (Guchar)code1; - remain = 1; - } else if (code1 == 256) { - endOfBlock = gTrue; - remain = 0; - } else { - code1 -= 257; - code2 = lengthDecode[code1].bits; - if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) - goto err; - len = lengthDecode[code1].first + code2; - if ((code1 = getHuffmanCodeWord(&distCodeTab)) == EOF) - goto err; - code2 = distDecode[code1].bits; - if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) - goto err; - dist = distDecode[code1].first + code2; - i = index; - j = (index - dist) & flateMask; - for (k = 0; k < len; ++k) { - buf[i] = buf[j]; - i = (i + 1) & flateMask; - j = (j + 1) & flateMask; - } - remain = len; - } - - } else { - len = (blockLen < flateWindow) ? blockLen : flateWindow; - for (i = 0, j = index; i < len; ++i, j = (j + 1) & flateMask) { - if ((c = str->getChar()) == EOF) { - endOfBlock = eof = gTrue; - break; - } - buf[j] = (Guchar)c; - } - remain = i; - blockLen -= len; - if (blockLen == 0) - endOfBlock = gTrue; - } - - return; - -err: - error(errSyntaxError, getPos(), "Unexpected end of file in flate stream"); - endOfBlock = eof = gTrue; - remain = 0; -} - -GBool FlateStream::startBlock() { - int blockHdr; - int c; - int check; - - // free the code tables from the previous block - if (litCodeTab.codes != fixedLitCodeTab.codes) { - gfree(litCodeTab.codes); - } - litCodeTab.codes = NULL; - if (distCodeTab.codes != fixedDistCodeTab.codes) { - gfree(distCodeTab.codes); - } - distCodeTab.codes = NULL; - - // read block header - blockHdr = getCodeWord(3); - if (blockHdr & 1) - eof = gTrue; - blockHdr >>= 1; - - // uncompressed block - if (blockHdr == 0) { - compressedBlock = gFalse; - if ((c = str->getChar()) == EOF) - goto err; - blockLen = c & 0xff; - if ((c = str->getChar()) == EOF) - goto err; - blockLen |= (c & 0xff) << 8; - if ((c = str->getChar()) == EOF) - goto err; - check = c & 0xff; - if ((c = str->getChar()) == EOF) - goto err; - check |= (c & 0xff) << 8; - if (check != (~blockLen & 0xffff)) - error(errSyntaxError, getPos(), - "Bad uncompressed block length in flate stream"); - codeBuf = 0; - codeSize = 0; - - // compressed block with fixed codes - } else if (blockHdr == 1) { - compressedBlock = gTrue; - loadFixedCodes(); - - // compressed block with dynamic codes - } else if (blockHdr == 2) { - compressedBlock = gTrue; - if (!readDynamicCodes()) { - goto err; - } - - // unknown block type - } else { - goto err; - } - - endOfBlock = gFalse; - return gTrue; - -err: - error(errSyntaxError, getPos(), "Bad block header in flate stream"); - endOfBlock = eof = gTrue; - return gFalse; -} - -void FlateStream::loadFixedCodes() { - litCodeTab.codes = fixedLitCodeTab.codes; - litCodeTab.maxLen = fixedLitCodeTab.maxLen; - distCodeTab.codes = fixedDistCodeTab.codes; - distCodeTab.maxLen = fixedDistCodeTab.maxLen; -} - -GBool FlateStream::readDynamicCodes() { - int numCodeLenCodes; - int numLitCodes; - int numDistCodes; - int codeLenCodeLengths[flateMaxCodeLenCodes]; - FlateHuffmanTab codeLenCodeTab; - int len, repeat, code; - int i; - - codeLenCodeTab.codes = NULL; - - // read lengths - if ((numLitCodes = getCodeWord(5)) == EOF) { - goto err; - } - numLitCodes += 257; - if ((numDistCodes = getCodeWord(5)) == EOF) { - goto err; - } - numDistCodes += 1; - if ((numCodeLenCodes = getCodeWord(4)) == EOF) { - goto err; - } - numCodeLenCodes += 4; - if (numLitCodes > flateMaxLitCodes || - numDistCodes > flateMaxDistCodes || - numCodeLenCodes > flateMaxCodeLenCodes) { - goto err; - } - - // build the code length code table - for (i = 0; i < flateMaxCodeLenCodes; ++i) { - codeLenCodeLengths[i] = 0; - } - for (i = 0; i < numCodeLenCodes; ++i) { - if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) { - goto err; - } - } - compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab); - - // build the literal and distance code tables - len = 0; - repeat = 0; - i = 0; - while (i < numLitCodes + numDistCodes) { - if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) { - goto err; - } - if (code == 16) { - if ((repeat = getCodeWord(2)) == EOF) { - goto err; - } - repeat += 3; - if (i + repeat > numLitCodes + numDistCodes) { - goto err; - } - for (; repeat > 0; --repeat) { - codeLengths[i++] = len; - } - } else if (code == 17) { - if ((repeat = getCodeWord(3)) == EOF) { - goto err; - } - repeat += 3; - if (i + repeat > numLitCodes + numDistCodes) { - goto err; - } - len = 0; - for (; repeat > 0; --repeat) { - codeLengths[i++] = 0; - } - } else if (code == 18) { - if ((repeat = getCodeWord(7)) == EOF) { - goto err; - } - repeat += 11; - if (i + repeat > numLitCodes + numDistCodes) { - goto err; - } - len = 0; - for (; repeat > 0; --repeat) { - codeLengths[i++] = 0; - } - } else { - codeLengths[i++] = len = code; - } - } - compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab); - compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab); - - gfree(codeLenCodeTab.codes); - return gTrue; - -err: - error(errSyntaxError, getPos(), "Bad dynamic code table in flate stream"); - gfree(codeLenCodeTab.codes); - return gFalse; -} - -// Convert an array of lengths, in value order, into a -// Huffman code lookup table. -void FlateStream::compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab) { - int tabSize, len, code, code2, skip, val, i, t; - - // find max code length - tab->maxLen = 0; - for (val = 0; val < n; ++val) { - if (lengths[val] > tab->maxLen) { - tab->maxLen = lengths[val]; - } - } - - // allocate the table - tabSize = 1 << tab->maxLen; - tab->codes = (FlateCode *)gmallocn(tabSize, sizeof(FlateCode)); - - // clear the table - for (i = 0; i < tabSize; ++i) { - tab->codes[i].len = 0; - tab->codes[i].val = 0; - } - - // build the table - for (len = 1, code = 0, skip = 2; - len <= tab->maxLen; - ++len, code <<= 1, skip <<= 1) { - for (val = 0; val < n; ++val) { - if (lengths[val] == len) { - - // bit-reverse the code - code2 = 0; - t = code; - for (i = 0; i < len; ++i) { - code2 = (code2 << 1) | (t & 1); - t >>= 1; - } - - // fill in the table entries - for (i = code2; i < tabSize; i += skip) { - tab->codes[i].len = (Gushort)len; - tab->codes[i].val = (Gushort)val; - } - - ++code; - } - } - } -} - -int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) { - FlateCode *code; - int c; - - while (codeSize < tab->maxLen) { - if ((c = str->getChar()) == EOF) { - break; - } - codeBuf |= (c & 0xff) << codeSize; - codeSize += 8; - } - code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)]; - if (codeSize == 0 || codeSize < code->len || code->len == 0) { - return EOF; - } - codeBuf >>= code->len; - codeSize -= code->len; - return (int)code->val; -} - -int FlateStream::getCodeWord(int bits) { - int c; - - while (codeSize < bits) { - if ((c = str->getChar()) == EOF) - return EOF; - codeBuf |= (c & 0xff) << codeSize; - codeSize += 8; - } - c = codeBuf & ((1 << bits) - 1); - codeBuf >>= bits; - codeSize -= bits; - return c; -} - -//------------------------------------------------------------------------ -// EOFStream -//------------------------------------------------------------------------ - -EOFStream::EOFStream(Stream *strA): - FilterStream(strA) { -} - -EOFStream::~EOFStream() { - delete str; -} - -Stream *EOFStream::copy() { - return new EOFStream(str->copy()); -} - -//------------------------------------------------------------------------ -// BufStream -//------------------------------------------------------------------------ - -BufStream::BufStream(Stream *strA, int bufSizeA): FilterStream(strA) { - bufSize = bufSizeA; - buf = (int *)gmallocn(bufSize, sizeof(int)); -} - -BufStream::~BufStream() { - gfree(buf); - delete str; -} - -Stream *BufStream::copy() { - return new BufStream(str->copy(), bufSize); -} - -void BufStream::reset() { - int i; - - str->reset(); - for (i = 0; i < bufSize; ++i) { - buf[i] = str->getChar(); - } -} - -int BufStream::getChar() { - int c, i; - - c = buf[0]; - for (i = 1; i < bufSize; ++i) { - buf[i-1] = buf[i]; - } - buf[bufSize - 1] = str->getChar(); - return c; -} - -int BufStream::lookChar() { - return buf[0]; -} - -int BufStream::lookChar(int idx) { - return buf[idx]; -} - -GBool BufStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -//------------------------------------------------------------------------ -// FixedLengthEncoder -//------------------------------------------------------------------------ - -FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA): - FilterStream(strA) { - length = lengthA; - count = 0; -} - -FixedLengthEncoder::~FixedLengthEncoder() { - if (str->isEncoder()) - delete str; -} - -Stream *FixedLengthEncoder::copy() { - error(errInternal, -1, "Called copy() on FixedLengthEncoder"); - return NULL; -} - -void FixedLengthEncoder::reset() { - str->reset(); - count = 0; -} - -int FixedLengthEncoder::getChar() { - if (length >= 0 && count >= length) - return EOF; - ++count; - return str->getChar(); -} - -int FixedLengthEncoder::lookChar() { - if (length >= 0 && count >= length) - return EOF; - return str->getChar(); -} - -GBool FixedLengthEncoder::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -//------------------------------------------------------------------------ -// ASCIIHexEncoder -//------------------------------------------------------------------------ - -ASCIIHexEncoder::ASCIIHexEncoder(Stream *strA): - FilterStream(strA) { - bufPtr = bufEnd = buf; - lineLen = 0; - eof = gFalse; -} - -ASCIIHexEncoder::~ASCIIHexEncoder() { - if (str->isEncoder()) { - delete str; - } -} - -Stream *ASCIIHexEncoder::copy() { - error(errInternal, -1, "Called copy() on ASCIIHexEncoder"); - return NULL; -} - -void ASCIIHexEncoder::reset() { - str->reset(); - bufPtr = bufEnd = buf; - lineLen = 0; - eof = gFalse; -} - -GBool ASCIIHexEncoder::fillBuf() { - static const char *hex = "0123456789abcdef"; - int c; - - if (eof) { - return gFalse; - } - bufPtr = bufEnd = buf; - if ((c = str->getChar()) == EOF) { - *bufEnd++ = '>'; - eof = gTrue; - } else { - if (lineLen >= 64) { - *bufEnd++ = '\n'; - lineLen = 0; - } - *bufEnd++ = hex[(c >> 4) & 0x0f]; - *bufEnd++ = hex[c & 0x0f]; - lineLen += 2; - } - return gTrue; -} - -//------------------------------------------------------------------------ -// ASCII85Encoder -//------------------------------------------------------------------------ - -ASCII85Encoder::ASCII85Encoder(Stream *strA): - FilterStream(strA) { - bufPtr = bufEnd = buf; - lineLen = 0; - eof = gFalse; -} - -ASCII85Encoder::~ASCII85Encoder() { - if (str->isEncoder()) - delete str; -} - -Stream *ASCII85Encoder::copy() { - error(errInternal, -1, "Called copy() on ASCII85Encoder"); - return NULL; -} - -void ASCII85Encoder::reset() { - str->reset(); - bufPtr = bufEnd = buf; - lineLen = 0; - eof = gFalse; -} - -GBool ASCII85Encoder::fillBuf() { - Guint t; - char buf1[5]; - int c0, c1, c2, c3; - int n, i; - - if (eof) { - return gFalse; - } - c0 = str->getChar(); - c1 = str->getChar(); - c2 = str->getChar(); - c3 = str->getChar(); - bufPtr = bufEnd = buf; - if (c3 == EOF) { - if (c0 == EOF) { - n = 0; - t = 0; - } else { - if (c1 == EOF) { - n = 1; - t = c0 << 24; - } else if (c2 == EOF) { - n = 2; - t = (c0 << 24) | (c1 << 16); - } else { - n = 3; - t = (c0 << 24) | (c1 << 16) | (c2 << 8); - } - for (i = 4; i >= 0; --i) { - buf1[i] = (char)(t % 85 + 0x21); - t /= 85; - } - for (i = 0; i <= n; ++i) { - *bufEnd++ = buf1[i]; - if (++lineLen == 65) { - *bufEnd++ = '\n'; - lineLen = 0; - } - } - } - *bufEnd++ = '~'; - *bufEnd++ = '>'; - eof = gTrue; - } else { - t = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3; - if (t == 0) { - *bufEnd++ = 'z'; - if (++lineLen == 65) { - *bufEnd++ = '\n'; - lineLen = 0; - } - } else { - for (i = 4; i >= 0; --i) { - buf1[i] = (char)(t % 85 + 0x21); - t /= 85; - } - for (i = 0; i <= 4; ++i) { - *bufEnd++ = buf1[i]; - if (++lineLen == 65) { - *bufEnd++ = '\n'; - lineLen = 0; - } - } - } - } - return gTrue; -} - -//------------------------------------------------------------------------ -// RunLengthEncoder -//------------------------------------------------------------------------ - -RunLengthEncoder::RunLengthEncoder(Stream *strA): - FilterStream(strA) { - bufPtr = bufEnd = nextEnd = buf; - eof = gFalse; -} - -RunLengthEncoder::~RunLengthEncoder() { - if (str->isEncoder()) - delete str; -} - -Stream *RunLengthEncoder::copy() { - error(errInternal, -1, "Called copy() on RunLengthEncoder"); - return NULL; -} - -void RunLengthEncoder::reset() { - str->reset(); - bufPtr = bufEnd = nextEnd = buf; - eof = gFalse; -} - -// -// When fillBuf finishes, buf[] looks like this: -// +-----+--------------+-----------------+-- -// + tag | ... data ... | next 0, 1, or 2 | -// +-----+--------------+-----------------+-- -// ^ ^ ^ -// bufPtr bufEnd nextEnd -// -GBool RunLengthEncoder::fillBuf() { - int c, c1, c2; - int n; - - // already hit EOF? - if (eof) - return gFalse; - - // grab two bytes - if (nextEnd < bufEnd + 1) { - if ((c1 = str->getChar()) == EOF) { - eof = gTrue; - return gFalse; - } - } else { - c1 = bufEnd[0] & 0xff; - } - if (nextEnd < bufEnd + 2) { - if ((c2 = str->getChar()) == EOF) { - eof = gTrue; - buf[0] = 0; - buf[1] = (char)c1; - bufPtr = buf; - bufEnd = &buf[2]; - return gTrue; - } - } else { - c2 = bufEnd[1] & 0xff; - } - - // check for repeat - c = 0; // make gcc happy - if (c1 == c2) { - n = 2; - while (n < 128 && (c = str->getChar()) == c1) - ++n; - buf[0] = (char)(257 - n); - buf[1] = (char)c1; - bufEnd = &buf[2]; - if (c == EOF) { - eof = gTrue; - } else if (n < 128) { - buf[2] = (char)c; - nextEnd = &buf[3]; - } else { - nextEnd = bufEnd; - } - - // get up to 128 chars - } else { - buf[1] = (char)c1; - buf[2] = (char)c2; - n = 2; - while (n < 128) { - if ((c = str->getChar()) == EOF) { - eof = gTrue; - break; - } - ++n; - buf[n] = (char)c; - if (buf[n] == buf[n-1]) - break; - } - if (buf[n] == buf[n-1]) { - buf[0] = (char)(n-2-1); - bufEnd = &buf[n-1]; - nextEnd = &buf[n+1]; - } else { - buf[0] = (char)(n-1); - bufEnd = nextEnd = &buf[n+1]; - } - } - bufPtr = buf; - return gTrue; -} - -//------------------------------------------------------------------------ -// LZWEncoder -//------------------------------------------------------------------------ - -LZWEncoder::LZWEncoder(Stream *strA): - FilterStream(strA) -{ - inBufStart = 0; - inBufLen = 0; - outBufLen = 0; -} - -LZWEncoder::~LZWEncoder() { - if (str->isEncoder()) { - delete str; - } -} - -Stream *LZWEncoder::copy() { - error(errInternal, -1, "Called copy() on LZWEncoder"); - return NULL; -} - -void LZWEncoder::reset() { - int i; - - str->reset(); - - // initialize code table - for (i = 0; i < 256; ++i) { - table[i].byte = i; - table[i].next = NULL; - table[i].children = NULL; - } - nextSeq = 258; - codeLen = 9; - - // initialize input buffer - inBufLen = str->getBlock((char *)inBuf, sizeof(inBuf)); - inBufStart = 0; - - // initialize output buffer with a clear-table code - outBuf = 256; - outBufLen = 9; - needEOD = gFalse; -} - -int LZWEncoder::getChar() { - int ret; - - if (inBufLen == 0 && !needEOD && outBufLen == 0) { - return EOF; - } - if (outBufLen < 8 && (inBufLen > 0 || needEOD)) { - fillBuf(); - } - if (outBufLen >= 8) { - ret = (outBuf >> (outBufLen - 8)) & 0xff; - outBufLen -= 8; - } else { - ret = (outBuf << (8 - outBufLen)) & 0xff; - outBufLen = 0; - } - return ret; -} - -int LZWEncoder::lookChar() { - if (inBufLen == 0 && !needEOD && outBufLen == 0) { - return EOF; - } - if (outBufLen < 8 && (inBufLen > 0 || needEOD)) { - fillBuf(); - } - if (outBufLen >= 8) { - return (outBuf >> (outBufLen - 8)) & 0xff; - } else { - return (outBuf << (8 - outBufLen)) & 0xff; - } -} - -// On input, outBufLen < 8. -// This function generates, at most, 2 12-bit codes -// --> outBufLen < 8 + 12 + 12 = 32 -void LZWEncoder::fillBuf() { - LZWEncoderNode *p0, *p1; - int seqLen, code, i; - - if (needEOD) { - outBuf = (outBuf << codeLen) | 257; - outBufLen += codeLen; - needEOD = gFalse; - return; - } - - // find longest matching sequence (if any) - p0 = table + inBuf[inBufStart]; - seqLen = 1; - while (inBufLen > seqLen) { - for (p1 = p0->children; p1; p1 = p1->next) { - if (p1->byte == inBuf[inBufStart + seqLen]) { - break; - } - } - if (!p1) { - break; - } - p0 = p1; - ++seqLen; - } - code = (int)(p0 - table); - - // generate an output code - outBuf = (outBuf << codeLen) | code; - outBufLen += codeLen; - - // update the table - table[nextSeq].byte = seqLen < inBufLen ? inBuf[inBufStart + seqLen] : 0; - table[nextSeq].children = NULL; - if (table[code].children) { - table[nextSeq].next = table[code].children; - } else { - table[nextSeq].next = NULL; - } - table[code].children = table + nextSeq; - ++nextSeq; - - // update the input buffer - inBufStart += seqLen; - inBufLen -= seqLen; - if (inBufStart >= 4096 && inBufStart + inBufLen == sizeof(inBuf)) { - memcpy(inBuf, inBuf + inBufStart, inBufLen); - inBufStart = 0; - inBufLen += str->getBlock((char *)inBuf + inBufLen, - (int)sizeof(inBuf) - inBufLen); - } - - // increment codeLen; generate clear-table code - if (nextSeq == (1 << codeLen)) { - ++codeLen; - if (codeLen == 13) { - outBuf = (outBuf << 12) | 256; - outBufLen += 12; - for (i = 0; i < 256; ++i) { - table[i].next = NULL; - table[i].children = NULL; - } - nextSeq = 258; - codeLen = 9; - } - } - - // generate EOD next time - if (inBufLen == 0) { - needEOD = gTrue; - } -} diff --git a/test/bug-hunting/cve/CVE-2019-10021/Stream.h b/test/bug-hunting/cve/CVE-2019-10021/Stream.h deleted file mode 100644 index 3c036e9c34d..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10021/Stream.h +++ /dev/null @@ -1,1189 +0,0 @@ -//======================================================================== -// -// Stream.h -// -// Copyright 1996-2003 Glyph & Cog, LLC -// -//======================================================================== - -#ifndef STREAM_H -#define STREAM_H - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include -#if HAVE_JPEGLIB -#include -#include -#endif -#include "gtypes.h" -#include "gfile.h" -#include "Object.h" - -class BaseStream; -class SharedFile; - -//------------------------------------------------------------------------ - -enum StreamKind { - strFile, - strASCIIHex, - strASCII85, - strLZW, - strRunLength, - strCCITTFax, - strDCT, - strFlate, - strJBIG2, - strJPX, - strWeird // internal-use stream types -}; - -enum StreamColorSpaceMode { - streamCSNone, - streamCSDeviceGray, - streamCSDeviceRGB, - streamCSDeviceCMYK -}; - -//------------------------------------------------------------------------ - -// This is in Stream.h instead of Decrypt.h to avoid really annoying -// include file dependency loops. -enum CryptAlgorithm { - cryptRC4, - cryptAES, - cryptAES256 -}; - -//------------------------------------------------------------------------ -// Stream (base class) -//------------------------------------------------------------------------ - -class Stream { -public: - - // Constructor. - Stream(); - - // Destructor. - virtual ~Stream(); - - virtual Stream *copy() = 0; - - // Get kind of stream. - virtual StreamKind getKind() = 0; - - virtual GBool isEmbedStream() { - return gFalse; - } - - // Reset stream to beginning. - virtual void reset() = 0; - - // Close down the stream. - virtual void close(); - - // Get next char from stream. - virtual int getChar() = 0; - - // Peek at next char in stream. - virtual int lookChar() = 0; - - // Get next char from stream without using the predictor. - // This is only used by StreamPredictor. - virtual int getRawChar(); - - // Get exactly bytes from stream. Returns the number of - // bytes read -- the returned count will be less than at EOF. - virtual int getBlock(char *blk, int size); - - // Get next line from stream. - virtual char *getLine(char *buf, int size); - - // Discard the next bytes from stream. Returns the number of - // bytes discarded, which will be less than only if EOF is - // reached. - virtual Guint discardChars(Guint n); - - // Get current position in file. - virtual GFileOffset getPos() = 0; - - // Go to a position in the stream. If is negative, the - // position is from the end of the file; otherwise the position is - // from the start of the file. - virtual void setPos(GFileOffset pos, int dir = 0) = 0; - - // Get PostScript command for the filter(s). - virtual GString *getPSFilter(int psLevel, const char *indent); - - // Does this stream type potentially contain non-printable chars? - virtual GBool isBinary(GBool last = gTrue) = 0; - - // Get the BaseStream of this stream. - virtual BaseStream *getBaseStream() = 0; - - // Get the stream after the last decoder (this may be a BaseStream - // or a DecryptStream). - virtual Stream *getUndecodedStream() = 0; - - // Get the dictionary associated with this stream. - virtual Dict *getDict() = 0; - - // Is this an encoding filter? - virtual GBool isEncoder() { - return gFalse; - } - - // Get image parameters which are defined by the stream contents. - virtual void getImageParams(int *bitsPerComponent, - StreamColorSpaceMode *csMode) {} - - // Return the next stream in the "stack". - virtual Stream *getNextStream() { - return NULL; - } - - // Add filters to this stream according to the parameters in . - // Returns the new stream. - Stream *addFilters(Object *dict, int recursion = 0); - -private: - - Stream *makeFilter(char *name, Stream *str, Object *params, int recursion); -}; - -//------------------------------------------------------------------------ -// BaseStream -// -// This is the base class for all streams that read directly from a file. -//------------------------------------------------------------------------ - -class BaseStream : public Stream { -public: - - BaseStream(Object *dictA); - virtual ~BaseStream(); - virtual Stream *makeSubStream(GFileOffset start, GBool limited, - GFileOffset length, Object *dict) = 0; - virtual void setPos(GFileOffset pos, int dir = 0) = 0; - virtual GBool isBinary(GBool last = gTrue) { - return last; - } - virtual BaseStream *getBaseStream() { - return this; - } - virtual Stream *getUndecodedStream() { - return this; - } - virtual Dict *getDict() { - return dict.getDict(); - } - virtual GString *getFileName() { - return NULL; - } - - // Get/set position of first byte of stream within the file. - virtual GFileOffset getStart() = 0; - virtual void moveStart(int delta) = 0; - -protected: - - Object dict; -}; - -//------------------------------------------------------------------------ -// FilterStream -// -// This is the base class for all streams that filter another stream. -//------------------------------------------------------------------------ - -class FilterStream : public Stream { -public: - - FilterStream(Stream *strA); - virtual ~FilterStream(); - virtual void close(); - virtual GFileOffset getPos() { - return str->getPos(); - } - virtual void setPos(GFileOffset pos, int dir = 0); - virtual BaseStream *getBaseStream() { - return str->getBaseStream(); - } - virtual Stream *getUndecodedStream() { - return str->getUndecodedStream(); - } - virtual Dict *getDict() { - return str->getDict(); - } - virtual Stream *getNextStream() { - return str; - } - -protected: - - Stream *str; -}; - -//------------------------------------------------------------------------ -// ImageStream -//------------------------------------------------------------------------ - -class ImageStream { -public: - - // Create an image stream object for an image with the specified - // parameters. Note that these are the actual image parameters, - // which may be different from the predictor parameters. - ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA); - - ~ImageStream(); - - // Reset the stream. - void reset(); - - // Close down the stream. - void close(); - - // Gets the next pixel from the stream. should be able to hold - // at least nComps elements. Returns false at end of file. - GBool getPixel(Guchar *pix); - - // Returns a pointer to the next line of pixels. Returns NULL at - // end of file. - Guchar *getLine(); - - // Skip an entire line from the image. - void skipLine(); - -private: - - Stream *str; // base stream - int width; // pixels per line - int nComps; // components per pixel - int nBits; // bits per component - int nVals; // components per line - int inputLineSize; // input line buffer size - char *inputLine; // input line buffer - Guchar *imgLine; // line buffer - int imgIdx; // current index in imgLine -}; - - -//------------------------------------------------------------------------ -// StreamPredictor -//------------------------------------------------------------------------ - -class StreamPredictor { -public: - - // Create a predictor object. Note that the parameters are for the - // predictor, and may not match the actual image parameters. - StreamPredictor(Stream *strA, int predictorA, - int widthA, int nCompsA, int nBitsA); - - ~StreamPredictor(); - - GBool isOk() { - return ok; - } - - void reset(); - - int lookChar(); - int getChar(); - int getBlock(char *blk, int size); - - int getPredictor() { - return predictor; - } - int getWidth() { - return width; - } - int getNComps() { - return nComps; - } - int getNBits() { - return nBits; - } - -private: - - GBool getNextLine(); - - Stream *str; // base stream - int predictor; // predictor - int width; // pixels per line - int nComps; // components per pixel - int nBits; // bits per component - int nVals; // components per line - int pixBytes; // bytes per pixel - int rowBytes; // bytes per line - Guchar *predLine; // line buffer - int predIdx; // current index in predLine - GBool ok; -}; - -//------------------------------------------------------------------------ -// FileStream -//------------------------------------------------------------------------ - -#define fileStreamBufSize 256 - -class FileStream : public BaseStream { -public: - - FileStream(FILE *fA, GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA); - virtual ~FileStream(); - virtual Stream *copy(); - virtual Stream *makeSubStream(GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA); - virtual StreamKind getKind() { - return strFile; - } - virtual void reset(); - virtual int getChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); - } - virtual int lookChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); - } - virtual int getBlock(char *blk, int size); - virtual GFileOffset getPos() { - return bufPos + (int)(bufPtr - buf); - } - virtual void setPos(GFileOffset pos, int dir = 0); - virtual GFileOffset getStart() { - return start; - } - virtual void moveStart(int delta); - -private: - - FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA); - GBool fillBuf(); - - SharedFile *f; - GFileOffset start; - GBool limited; - GFileOffset length; - char buf[fileStreamBufSize]; - char *bufPtr; - char *bufEnd; - GFileOffset bufPos; -}; - -//------------------------------------------------------------------------ -// MemStream -//------------------------------------------------------------------------ - -class MemStream : public BaseStream { -public: - - MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA); - virtual ~MemStream(); - virtual Stream *copy(); - virtual Stream *makeSubStream(GFileOffset start, GBool limited, - GFileOffset lengthA, Object *dictA); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual void close(); - virtual int getChar() - { - return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; - } - virtual int lookChar() - { - return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; - } - virtual int getBlock(char *blk, int size); - virtual GFileOffset getPos() { - return (GFileOffset)(bufPtr - buf); - } - virtual void setPos(GFileOffset pos, int dir = 0); - virtual GFileOffset getStart() { - return start; - } - virtual void moveStart(int delta); - -private: - - char *buf; - Guint start; - Guint length; - char *bufEnd; - char *bufPtr; - GBool needFree; -}; - -//------------------------------------------------------------------------ -// EmbedStream -// -// This is a special stream type used for embedded streams (inline -// images). It reads directly from the base stream -- after the -// EmbedStream is deleted, reads from the base stream will proceed where -// the BaseStream left off. Note that this is very different behavior -// that creating a new FileStream (using makeSubStream). -//------------------------------------------------------------------------ - -class EmbedStream : public BaseStream { -public: - - EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA); - virtual ~EmbedStream(); - virtual Stream *copy(); - virtual Stream *makeSubStream(GFileOffset start, GBool limitedA, - GFileOffset lengthA, Object *dictA); - virtual StreamKind getKind() { - return str->getKind(); - } - virtual GBool isEmbedStream() { - return gTrue; - } - virtual void reset() {} - virtual int getChar(); - virtual int lookChar(); - virtual int getBlock(char *blk, int size); - virtual GFileOffset getPos() { - return str->getPos(); - } - virtual void setPos(GFileOffset pos, int dir = 0); - virtual GFileOffset getStart(); - virtual void moveStart(int delta); - -private: - - Stream *str; - GBool limited; - GFileOffset length; -}; - -//------------------------------------------------------------------------ -// ASCIIHexStream -//------------------------------------------------------------------------ - -class ASCIIHexStream : public FilterStream { -public: - - ASCIIHexStream(Stream *strA); - virtual ~ASCIIHexStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strASCIIHex; - } - virtual void reset(); - virtual int getChar() - { - int c = lookChar(); buf = EOF; return c; - } - virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - int buf; - GBool eof; -}; - -//------------------------------------------------------------------------ -// ASCII85Stream -//------------------------------------------------------------------------ - -class ASCII85Stream : public FilterStream { -public: - - ASCII85Stream(Stream *strA); - virtual ~ASCII85Stream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strASCII85; - } - virtual void reset(); - virtual int getChar() - { - int ch = lookChar(); ++index; return ch; - } - virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - int c[5]; - int b[4]; - int index, n; - GBool eof; -}; - -//------------------------------------------------------------------------ -// LZWStream -//------------------------------------------------------------------------ - -class LZWStream : public FilterStream { -public: - - LZWStream(Stream *strA, int predictor, int columns, int colors, - int bits, int earlyA); - virtual ~LZWStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strLZW; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual int getRawChar(); - virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - StreamPredictor *pred; // predictor - int early; // early parameter - GBool eof; // true if at eof - int inputBuf; // input buffer - int inputBits; // number of bits in input buffer - struct { // decoding table - int length; - int head; - Guchar tail; - } table[4097]; - int nextCode; // next code to be used - int nextBits; // number of bits in next code word - int prevCode; // previous code used in stream - int newChar; // next char to be added to table - Guchar seqBuf[4097]; // buffer for current sequence - int seqLength; // length of current sequence - int seqIndex; // index into current sequence - GBool first; // first code after a table clear - - GBool processNextCode(); - void clearTable(); - int getCode(); -}; - -//------------------------------------------------------------------------ -// RunLengthStream -//------------------------------------------------------------------------ - -class RunLengthStream : public FilterStream { -public: - - RunLengthStream(Stream *strA); - virtual ~RunLengthStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strRunLength; - } - virtual void reset(); - virtual int getChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); - } - virtual int lookChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); - } - virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - char buf[128]; // buffer - char *bufPtr; // next char to read - char *bufEnd; // end of buffer - GBool eof; - - GBool fillBuf(); -}; - -//------------------------------------------------------------------------ -// CCITTFaxStream -//------------------------------------------------------------------------ - -struct CCITTCodeTable; - -class CCITTFaxStream : public FilterStream { -public: - - CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, - GBool byteAlignA, int columnsA, int rowsA, - GBool endOfBlockA, GBool blackA); - virtual ~CCITTFaxStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strCCITTFax; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - int encoding; // 'K' parameter - GBool endOfLine; // 'EndOfLine' parameter - GBool byteAlign; // 'EncodedByteAlign' parameter - int columns; // 'Columns' parameter - int rows; // 'Rows' parameter - GBool endOfBlock; // 'EndOfBlock' parameter - GBool black; // 'BlackIs1' parameter - int blackXOR; - GBool eof; // true if at eof - GBool nextLine2D; // true if next line uses 2D encoding - int row; // current row - Guint inputBuf; // input buffer - int inputBits; // number of bits in input buffer - int *codingLine; // coding line changing elements - int *refLine; // reference line changing elements - int nextCol; // next column to read - int a0i; // index into codingLine - GBool err; // error on current line - int nErrors; // number of errors so far in this stream - - void addPixels(int a1, int blackPixels); - void addPixelsNeg(int a1, int blackPixels); - GBool readRow(); - short getTwoDimCode(); - short getWhiteCode(); - short getBlackCode(); - short lookBits(int n); - void eatBits(int n) { - if ((inputBits -= n) < 0) inputBits = 0; - } -}; - -//------------------------------------------------------------------------ -// DCTStream -//------------------------------------------------------------------------ - -#if HAVE_JPEGLIB - -class DCTStream; - -#define dctStreamBufSize 4096 - -struct DCTSourceMgr { - jpeg_source_mgr src; - DCTStream *str; - char buf[dctStreamBufSize]; -}; - -struct DCTErrorMgr { - struct jpeg_error_mgr err; - jmp_buf setjmpBuf; -}; - -#else // HAVE_JPEGLIB - -// DCT component info -struct DCTCompInfo { - int id; // component ID - int hSample, vSample; // horiz/vert sampling resolutions - int quantTable; // quantization table number - int prevDC; // DC coefficient accumulator -}; - -struct DCTScanInfo { - GBool comp[4]; // comp[i] is set if component i is - // included in this scan - int numComps; // number of components in the scan - int dcHuffTable[4]; // DC Huffman table numbers - int acHuffTable[4]; // AC Huffman table numbers - int firstCoeff, lastCoeff; // first and last DCT coefficient - int ah, al; // successive approximation parameters -}; - -// DCT Huffman decoding table -struct DCTHuffTable { - Guchar firstSym[17]; // first symbol for this bit length - Gushort firstCode[17]; // first code for this bit length - Gushort numCodes[17]; // number of codes of this bit length - Guchar sym[256]; // symbols -}; - -#endif // HAVE_JPEGLIB - -class DCTStream : public FilterStream { -public: - - DCTStream(Stream *strA, int colorXformA); - virtual ~DCTStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strDCT; - } - virtual void reset(); - virtual void close(); - virtual int getChar(); - virtual int lookChar(); - virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - Stream *getRawStream() { - return str; - } - -private: - -#if HAVE_JPEGLIB - - int colorXform; // color transform: -1 = unspecified - // 0 = none - // 1 = YUV/YUVK -> RGB/CMYK - jpeg_decompress_struct decomp; - DCTErrorMgr errorMgr; - DCTSourceMgr sourceMgr; - GBool error; - char *lineBuf; - int lineBufHeight; - char *lineBufRows[4]; - char *bufPtr; - char *bufEnd; - GBool inlineImage; - - GBool fillBuf(); - static void errorExit(j_common_ptr d); - static void errorMessage(j_common_ptr d); - static void initSourceCbk(j_decompress_ptr d); - static boolean fillInputBufferCbk(j_decompress_ptr d); - static void skipInputDataCbk(j_decompress_ptr d, long numBytes); - static void termSourceCbk(j_decompress_ptr d); - -#else // HAVE_JPEGLIB - - GBool progressive; // set if in progressive mode - GBool interleaved; // set if in interleaved mode - int width, height; // image size - int mcuWidth, mcuHeight; // size of min coding unit, in data units - int bufWidth, bufHeight; // frameBuf size - DCTCompInfo compInfo[4]; // info for each component - DCTScanInfo scanInfo; // info for the current scan - int numComps; // number of components in image - int colorXform; // color transform: -1 = unspecified - // 0 = none - // 1 = YUV/YUVK -> RGB/CMYK - GBool gotJFIFMarker; // set if APP0 JFIF marker was present - GBool gotAdobeMarker; // set if APP14 Adobe marker was present - int restartInterval; // restart interval, in MCUs - Gushort quantTables[4][64]; // quantization tables - int numQuantTables; // number of quantization tables - DCTHuffTable dcHuffTables[4]; // DC Huffman tables - DCTHuffTable acHuffTables[4]; // AC Huffman tables - int numDCHuffTables; // number of DC Huffman tables - int numACHuffTables; // number of AC Huffman tables - Guchar *rowBuf; - Guchar *rowBufPtr; // current position within rowBuf - Guchar *rowBufEnd; // end of valid data in rowBuf - int *frameBuf[4]; // buffer for frame (progressive mode) - int comp, x, y; // current position within image/MCU - int restartCtr; // MCUs left until restart - int restartMarker; // next restart marker - int eobRun; // number of EOBs left in the current run - int inputBuf; // input buffer for variable length codes - int inputBits; // number of valid bits in input buffer - - void restart(); - GBool readMCURow(); - void readScan(); - GBool readDataUnit(DCTHuffTable *dcHuffTable, - DCTHuffTable *acHuffTable, - int *prevDC, int data[64]); - GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable, - DCTHuffTable *acHuffTable, - int *prevDC, int data[64]); - void decodeImage(); - void transformDataUnit(Gushort *quantTable, - int dataIn[64], Guchar dataOut[64]); - int readHuffSym(DCTHuffTable *table); - int readAmp(int size); - int readBit(); - GBool readHeader(GBool frame); - GBool readBaselineSOF(); - GBool readProgressiveSOF(); - GBool readScanInfo(); - GBool readQuantTables(); - GBool readHuffmanTables(); - GBool readRestartInterval(); - GBool readJFIFMarker(); - GBool readAdobeMarker(); - GBool readTrailer(); - int readMarker(); - int read16(); - -#endif // HAVE_JPEGLIB -}; - -//------------------------------------------------------------------------ -// FlateStream -//------------------------------------------------------------------------ - -#define flateWindow 32768 // buffer size -#define flateMask (flateWindow-1) -#define flateMaxHuffman 15 // max Huffman code length -#define flateMaxCodeLenCodes 19 // max # code length codes -#define flateMaxLitCodes 288 // max # literal codes -#define flateMaxDistCodes 30 // max # distance codes - -// Huffman code table entry -struct FlateCode { - Gushort len; // code length, in bits - Gushort val; // value represented by this code -}; - -struct FlateHuffmanTab { - FlateCode *codes; - int maxLen; -}; - -// Decoding info for length and distance code words -struct FlateDecode { - int bits; // # extra bits - int first; // first length/distance -}; - -class FlateStream : public FilterStream { -public: - - FlateStream(Stream *strA, int predictor, int columns, - int colors, int bits); - virtual ~FlateStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strFlate; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual int getRawChar(); - virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - StreamPredictor *pred; // predictor - Guchar buf[flateWindow]; // output data buffer - int index; // current index into output buffer - int remain; // number valid bytes in output buffer - int codeBuf; // input buffer - int codeSize; // number of bits in input buffer - int // literal and distance code lengths - codeLengths[flateMaxLitCodes + flateMaxDistCodes]; - FlateHuffmanTab litCodeTab; // literal code table - FlateHuffmanTab distCodeTab; // distance code table - GBool compressedBlock; // set if reading a compressed block - int blockLen; // remaining length of uncompressed block - GBool endOfBlock; // set when end of block is reached - GBool eof; // set when end of stream is reached - - static int // code length code reordering - codeLenCodeMap[flateMaxCodeLenCodes]; - static FlateDecode // length decoding info - lengthDecode[flateMaxLitCodes-257]; - static FlateDecode // distance decoding info - distDecode[flateMaxDistCodes]; - static FlateHuffmanTab // fixed literal code table - fixedLitCodeTab; - static FlateHuffmanTab // fixed distance code table - fixedDistCodeTab; - - void readSome(); - GBool startBlock(); - void loadFixedCodes(); - GBool readDynamicCodes(); - void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab); - int getHuffmanCodeWord(FlateHuffmanTab *tab); - int getCodeWord(int bits); -}; - -//------------------------------------------------------------------------ -// EOFStream -//------------------------------------------------------------------------ - -class EOFStream : public FilterStream { -public: - - EOFStream(Stream *strA); - virtual ~EOFStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset() {} - virtual int getChar() { - return EOF; - } - virtual int lookChar() { - return EOF; - } - virtual int getBlock(char *blk, int size) { - return 0; - } - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue) { - return gFalse; - } -}; - -//------------------------------------------------------------------------ -// BufStream -//------------------------------------------------------------------------ - -class BufStream : public FilterStream { -public: - - BufStream(Stream *strA, int bufSizeA); - virtual ~BufStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue); - - int lookChar(int idx); - -private: - - int *buf; - int bufSize; -}; - -//------------------------------------------------------------------------ -// FixedLengthEncoder -//------------------------------------------------------------------------ - -class FixedLengthEncoder : public FilterStream { -public: - - FixedLengthEncoder(Stream *strA, int lengthA); - ~FixedLengthEncoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue); - virtual GBool isEncoder() { - return gTrue; - } - -private: - - int length; - int count; -}; - -//------------------------------------------------------------------------ -// ASCIIHexEncoder -//------------------------------------------------------------------------ - -class ASCIIHexEncoder : public FilterStream { -public: - - ASCIIHexEncoder(Stream *strA); - virtual ~ASCIIHexEncoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); - } - virtual int lookChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); - } - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue) { - return gFalse; - } - virtual GBool isEncoder() { - return gTrue; - } - -private: - - char buf[4]; - char *bufPtr; - char *bufEnd; - int lineLen; - GBool eof; - - GBool fillBuf(); -}; - -//------------------------------------------------------------------------ -// ASCII85Encoder -//------------------------------------------------------------------------ - -class ASCII85Encoder : public FilterStream { -public: - - ASCII85Encoder(Stream *strA); - virtual ~ASCII85Encoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); - } - virtual int lookChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); - } - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue) { - return gFalse; - } - virtual GBool isEncoder() { - return gTrue; - } - -private: - - char buf[8]; - char *bufPtr; - char *bufEnd; - int lineLen; - GBool eof; - - GBool fillBuf(); -}; - -//------------------------------------------------------------------------ -// RunLengthEncoder -//------------------------------------------------------------------------ - -class RunLengthEncoder : public FilterStream { -public: - - RunLengthEncoder(Stream *strA); - virtual ~RunLengthEncoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); - } - virtual int lookChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); - } - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue) { - return gTrue; - } - virtual GBool isEncoder() { - return gTrue; - } - -private: - - char buf[131]; - char *bufPtr; - char *bufEnd; - char *nextEnd; - GBool eof; - - GBool fillBuf(); -}; - -//------------------------------------------------------------------------ -// LZWEncoder -//------------------------------------------------------------------------ - -struct LZWEncoderNode { - int byte; - LZWEncoderNode *next; // next sibling - LZWEncoderNode *children; // first child -}; - -class LZWEncoder : public FilterStream { -public: - - LZWEncoder(Stream *strA); - virtual ~LZWEncoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue) { - return gTrue; - } - virtual GBool isEncoder() { - return gTrue; - } - -private: - - LZWEncoderNode table[4096]; - int nextSeq; - int codeLen; - Guchar inBuf[8192]; - int inBufStart; - int inBufLen; - int outBuf; - int outBufLen; - GBool needEOD; - - void fillBuf(); -}; - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-10021/expected.txt b/test/bug-hunting/cve/CVE-2019-10021/expected.txt deleted file mode 100644 index 9b67b40ef6e..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10021/expected.txt +++ /dev/null @@ -1 +0,0 @@ -Stream.cc:359:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-10023/Function.cc b/test/bug-hunting/cve/CVE-2019-10023/Function.cc deleted file mode 100644 index 72cadd9bed1..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10023/Function.cc +++ /dev/null @@ -1,1567 +0,0 @@ -//======================================================================== -// -// Function.cc -// -// Copyright 2001-2003 Glyph & Cog, LLC -// -//======================================================================== - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include -#include -#include -#include -#include "gmem.h" -#include "gmempp.h" -#include "GList.h" -#include "Object.h" -#include "Dict.h" -#include "Stream.h" -#include "Error.h" -#include "Function.h" - -//------------------------------------------------------------------------ - -// Max depth of nested functions. This is used to catch infinite -// loops in the function object structure. -#define recursionLimit 8 - -//------------------------------------------------------------------------ -// Function -//------------------------------------------------------------------------ - -Function::Function() { -} - -Function::~Function() { -} - -Function *Function::parse(Object *funcObj, int recursion) { - Function *func; - Dict *dict; - int funcType; - Object obj1; - - if (recursion > recursionLimit) { - error(errSyntaxError, -1, "Loop detected in function objects"); - return NULL; - } - - if (funcObj->isStream()) { - dict = funcObj->streamGetDict(); - } else if (funcObj->isDict()) { - dict = funcObj->getDict(); - } else if (funcObj->isName("Identity")) { - return new IdentityFunction(); - } else { - error(errSyntaxError, -1, "Expected function dictionary or stream"); - return NULL; - } - - if (!dict->lookup("FunctionType", &obj1)->isInt()) { - error(errSyntaxError, -1, "Function type is missing or wrong type"); - obj1.free(); - return NULL; - } - funcType = obj1.getInt(); - obj1.free(); - - if (funcType == 0) { - func = new SampledFunction(funcObj, dict); - } else if (funcType == 2) { - func = new ExponentialFunction(funcObj, dict); - } else if (funcType == 3) { - func = new StitchingFunction(funcObj, dict, recursion); - } else if (funcType == 4) { - func = new PostScriptFunction(funcObj, dict); - } else { - error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType); - return NULL; - } - if (!func->isOk()) { - delete func; - return NULL; - } - - return func; -} - -GBool Function::init(Dict *dict) { - Object obj1, obj2; - int i; - - //----- Domain - if (!dict->lookup("Domain", &obj1)->isArray()) { - error(errSyntaxError, -1, "Function is missing domain"); - goto err2; - } - m = obj1.arrayGetLength() / 2; - if (m > funcMaxInputs) { - error(errSyntaxError, -1, - "Functions with more than {0:d} inputs are unsupported", - funcMaxInputs); - goto err2; - } - for (i = 0; i < m; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function domain array"); - goto err1; - } - domain[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function domain array"); - goto err1; - } - domain[i][1] = obj2.getNum(); - obj2.free(); - } - obj1.free(); - - //----- Range - hasRange = gFalse; - n = 0; - if (dict->lookup("Range", &obj1)->isArray()) { - hasRange = gTrue; - n = obj1.arrayGetLength() / 2; - if (n > funcMaxOutputs) { - error(errSyntaxError, -1, - "Functions with more than {0:d} outputs are unsupported", - funcMaxOutputs); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function range array"); - goto err1; - } - range[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function range array"); - goto err1; - } - range[i][1] = obj2.getNum(); - obj2.free(); - } - } - obj1.free(); - - return gTrue; - - err1: - obj2.free(); - err2: - obj1.free(); - return gFalse; -} - -//------------------------------------------------------------------------ -// IdentityFunction -//------------------------------------------------------------------------ - -IdentityFunction::IdentityFunction() { - int i; - - // fill these in with arbitrary values just in case they get used - // somewhere - m = funcMaxInputs; - n = funcMaxOutputs; - for (i = 0; i < funcMaxInputs; ++i) { - domain[i][0] = 0; - domain[i][1] = 1; - } - hasRange = gFalse; -} - -IdentityFunction::~IdentityFunction() { -} - -void IdentityFunction::transform(double *in, double *out) { - int i; - - for (i = 0; i < funcMaxOutputs; ++i) { - out[i] = in[i]; - } -} - -//------------------------------------------------------------------------ -// SampledFunction -//------------------------------------------------------------------------ - -SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { - Stream *str; - int sampleBits; - double sampleMul; - Object obj1, obj2; - Guint buf, bitMask; - int bits; - Guint s; - double in[funcMaxInputs]; - int i, j, t, bit, idx; - - idxOffset = NULL; - samples = NULL; - sBuf = NULL; - ok = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (!hasRange) { - error(errSyntaxError, -1, "Type 0 function is missing range"); - goto err1; - } - if (m > sampledFuncMaxInputs) { - error(errSyntaxError, -1, - "Sampled functions with more than {0:d} inputs are unsupported", - sampledFuncMaxInputs); - goto err1; - } - - //----- buffer - sBuf = (double *)gmallocn(1 << m, sizeof(double)); - - //----- get the stream - if (!funcObj->isStream()) { - error(errSyntaxError, -1, "Type 0 function isn't a stream"); - goto err1; - } - str = funcObj->getStream(); - - //----- Size - if (!dict->lookup("Size", &obj1)->isArray() || - obj1.arrayGetLength() != m) { - error(errSyntaxError, -1, "Function has missing or invalid size array"); - goto err2; - } - for (i = 0; i < m; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isInt()) { - error(errSyntaxError, -1, "Illegal value in function size array"); - goto err3; - } - sampleSize[i] = obj2.getInt(); - if (sampleSize[i] <= 0) { - error(errSyntaxError, -1, "Illegal non-positive value in function size array"); - goto err3; - } - obj2.free(); - } - obj1.free(); - idxOffset = (int *)gmallocn(1 << m, sizeof(int)); - for (i = 0; i < (1<= 1; --j, t <<= 1) { - if (sampleSize[j] == 1) { - bit = 0; - } else { - bit = (t >> (m - 1)) & 1; - } - idx = (idx + bit) * sampleSize[j-1]; - } - if (sampleSize[0] == 1) { - bit = 0; - } else { - bit = (t >> (m - 1)) & 1; - } - idxOffset[i] = (idx + bit) * n; - } - - //----- BitsPerSample - if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { - error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample"); - goto err2; - } - sampleBits = obj1.getInt(); - sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1); - obj1.free(); - - //----- Encode - if (dict->lookup("Encode", &obj1)->isArray() && - obj1.arrayGetLength() == 2*m) { - for (i = 0; i < m; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function encode array"); - goto err3; - } - encode[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function encode array"); - goto err3; - } - encode[i][1] = obj2.getNum(); - obj2.free(); - } - } else { - for (i = 0; i < m; ++i) { - encode[i][0] = 0; - encode[i][1] = sampleSize[i] - 1; - } - } - obj1.free(); - for (i = 0; i < m; ++i) { - inputMul[i] = (encode[i][1] - encode[i][0]) / - (domain[i][1] - domain[i][0]); - } - - //----- Decode - if (dict->lookup("Decode", &obj1)->isArray() && - obj1.arrayGetLength() == 2*n) { - for (i = 0; i < n; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function decode array"); - goto err3; - } - decode[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function decode array"); - goto err3; - } - decode[i][1] = obj2.getNum(); - obj2.free(); - } - } else { - for (i = 0; i < n; ++i) { - decode[i][0] = range[i][0]; - decode[i][1] = range[i][1]; - } - } - obj1.free(); - - //----- samples - nSamples = n; - for (i = 0; i < m; ++i) - nSamples *= sampleSize[i]; - samples = (double *)gmallocn(nSamples, sizeof(double)); - buf = 0; - bits = 0; - bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU; - str->reset(); - for (i = 0; i < nSamples; ++i) { - if (sampleBits == 8) { - s = str->getChar(); - } else if (sampleBits == 16) { - s = str->getChar(); - s = (s << 8) + str->getChar(); - } else if (sampleBits == 32) { - s = str->getChar(); - s = (s << 8) + str->getChar(); - s = (s << 8) + str->getChar(); - s = (s << 8) + str->getChar(); - } else { - while (bits < sampleBits) { - buf = (buf << 8) | (str->getChar() & 0xff); - bits += 8; - } - s = (buf >> (bits - sampleBits)) & bitMask; - bits -= sampleBits; - } - samples[i] = (double)s * sampleMul; - } - str->close(); - - // set up the cache - for (i = 0; i < m; ++i) { - in[i] = domain[i][0]; - cacheIn[i] = in[i] - 1; - } - transform(in, cacheOut); - - ok = gTrue; - return; - - err3: - obj2.free(); - err2: - obj1.free(); - err1: - return; -} - -SampledFunction::~SampledFunction() { - if (idxOffset) { - gfree(idxOffset); - } - if (samples) { - gfree(samples); - } - if (sBuf) { - gfree(sBuf); - } -} - -SampledFunction::SampledFunction(SampledFunction *func) { - memcpy((void *)this, (void *)func, sizeof(SampledFunction)); - idxOffset = (int *)gmallocn(1 << m, sizeof(int)); - memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int)); - samples = (double *)gmallocn(nSamples, sizeof(double)); - memcpy(samples, func->samples, nSamples * sizeof(double)); - sBuf = (double *)gmallocn(1 << m, sizeof(double)); -} - -void SampledFunction::transform(double *in, double *out) { - double x; - int e[funcMaxInputs]; - double efrac0[funcMaxInputs]; - double efrac1[funcMaxInputs]; - int i, j, k, idx0, t; - - // check the cache - for (i = 0; i < m; ++i) { - if (in[i] != cacheIn[i]) { - break; - } - } - if (i == m) { - for (i = 0; i < n; ++i) { - out[i] = cacheOut[i]; - } - return; - } - - // map input values into sample array - for (i = 0; i < m; ++i) { - x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0]; - if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x) - x = 0; - } else if (x > sampleSize[i] - 1) { - x = sampleSize[i] - 1; - } - e[i] = (int)x; - if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) { - // this happens if in[i] = domain[i][1] - e[i] = sampleSize[i] - 2; - } - efrac1[i] = x - e[i]; - efrac0[i] = 1 - efrac1[i]; - } - - // compute index for the first sample to be used - idx0 = 0; - for (k = m - 1; k >= 1; --k) { - idx0 = (idx0 + e[k]) * sampleSize[k-1]; - } - idx0 = (idx0 + e[0]) * n; - - // for each output, do m-linear interpolation - for (i = 0; i < n; ++i) { - - // pull 2^m values out of the sample array - for (j = 0; j < (1<>= 1) { - for (k = 0; k < t; k += 2) { - sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1]; - } - } - - // map output value to range - out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]; - if (out[i] < range[i][0]) { - out[i] = range[i][0]; - } else if (out[i] > range[i][1]) { - out[i] = range[i][1]; - } - } - - // save current result in the cache - for (i = 0; i < m; ++i) { - cacheIn[i] = in[i]; - } - for (i = 0; i < n; ++i) { - cacheOut[i] = out[i]; - } -} - -//------------------------------------------------------------------------ -// ExponentialFunction -//------------------------------------------------------------------------ - -ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { - Object obj1, obj2; - int i; - - ok = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (m != 1) { - error(errSyntaxError, -1, "Exponential function with more than one input"); - goto err1; - } - - //----- C0 - if (dict->lookup("C0", &obj1)->isArray()) { - if (hasRange && obj1.arrayGetLength() != n) { - error(errSyntaxError, -1, "Function's C0 array is wrong length"); - goto err2; - } - n = obj1.arrayGetLength(); - if (n > funcMaxOutputs) { - error(errSyntaxError, -1, - "Functions with more than {0:d} outputs are unsupported", - funcMaxOutputs); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function C0 array"); - goto err3; - } - c0[i] = obj2.getNum(); - obj2.free(); - } - } else { - if (hasRange && n != 1) { - error(errSyntaxError, -1, "Function's C0 array is wrong length"); - goto err2; - } - n = 1; - c0[0] = 0; - } - obj1.free(); - - //----- C1 - if (dict->lookup("C1", &obj1)->isArray()) { - if (obj1.arrayGetLength() != n) { - error(errSyntaxError, -1, "Function's C1 array is wrong length"); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function C1 array"); - goto err3; - } - c1[i] = obj2.getNum(); - obj2.free(); - } - } else { - if (n != 1) { - error(errSyntaxError, -1, "Function's C1 array is wrong length"); - goto err2; - } - c1[0] = 1; - } - obj1.free(); - - //----- N (exponent) - if (!dict->lookup("N", &obj1)->isNum()) { - error(errSyntaxError, -1, "Function has missing or invalid N"); - goto err2; - } - e = obj1.getNum(); - obj1.free(); - - ok = gTrue; - return; - - err3: - obj2.free(); - err2: - obj1.free(); - err1: - return; -} - -ExponentialFunction::~ExponentialFunction() { -} - -ExponentialFunction::ExponentialFunction(ExponentialFunction *func) { - memcpy((void *)this, (void *)func, sizeof(ExponentialFunction)); -} - -void ExponentialFunction::transform(double *in, double *out) { - double x; - int i; - - if (in[0] < domain[0][0]) { - x = domain[0][0]; - } else if (in[0] > domain[0][1]) { - x = domain[0][1]; - } else { - x = in[0]; - } - for (i = 0; i < n; ++i) { - out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]); - if (hasRange) { - if (out[i] < range[i][0]) { - out[i] = range[i][0]; - } else if (out[i] > range[i][1]) { - out[i] = range[i][1]; - } - } - } - return; -} - -//------------------------------------------------------------------------ -// StitchingFunction -//------------------------------------------------------------------------ - -StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, - int recursion) { - Object obj1, obj2; - int i; - - ok = gFalse; - funcs = NULL; - bounds = NULL; - encode = NULL; - scale = NULL; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (m != 1) { - error(errSyntaxError, -1, "Stitching function with more than one input"); - goto err1; - } - - //----- Functions - if (!dict->lookup("Functions", &obj1)->isArray()) { - error(errSyntaxError, -1, - "Missing 'Functions' entry in stitching function"); - goto err1; - } - k = obj1.arrayGetLength(); - funcs = (Function **)gmallocn(k, sizeof(Function *)); - bounds = (double *)gmallocn(k + 1, sizeof(double)); - encode = (double *)gmallocn(2 * k, sizeof(double)); - scale = (double *)gmallocn(k, sizeof(double)); - for (i = 0; i < k; ++i) { - funcs[i] = NULL; - } - for (i = 0; i < k; ++i) { - if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2), - recursion + 1))) { - goto err2; - } - if (funcs[i]->getInputSize() != 1 || - (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { - error(errSyntaxError, -1, - "Incompatible subfunctions in stitching function"); - goto err2; - } - obj2.free(); - } - obj1.free(); - - //----- Bounds - if (!dict->lookup("Bounds", &obj1)->isArray() || - obj1.arrayGetLength() != k - 1) { - error(errSyntaxError, -1, - "Missing or invalid 'Bounds' entry in stitching function"); - goto err1; - } - bounds[0] = domain[0][0]; - for (i = 1; i < k; ++i) { - if (!obj1.arrayGet(i - 1, &obj2)->isNum()) { - error(errSyntaxError, -1, - "Invalid type in 'Bounds' array in stitching function"); - goto err2; - } - bounds[i] = obj2.getNum(); - obj2.free(); - } - bounds[k] = domain[0][1]; - obj1.free(); - - //----- Encode - if (!dict->lookup("Encode", &obj1)->isArray() || - obj1.arrayGetLength() != 2 * k) { - error(errSyntaxError, -1, - "Missing or invalid 'Encode' entry in stitching function"); - goto err1; - } - for (i = 0; i < 2 * k; ++i) { - if (!obj1.arrayGet(i, &obj2)->isNum()) { - error(errSyntaxError, -1, - "Invalid type in 'Encode' array in stitching function"); - goto err2; - } - encode[i] = obj2.getNum(); - obj2.free(); - } - obj1.free(); - - //----- pre-compute the scale factors - for (i = 0; i < k; ++i) { - if (bounds[i] == bounds[i+1]) { - // avoid a divide-by-zero -- in this situation, function i will - // never be used anyway - scale[i] = 0; - } else { - scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]); - } - } - - ok = gTrue; - return; - - err2: - obj2.free(); - err1: - obj1.free(); -} - -StitchingFunction::StitchingFunction(StitchingFunction *func) { - int i; - - memcpy((void *)this, (void *)func, sizeof(StitchingFunction)); - funcs = (Function **)gmallocn(k, sizeof(Function *)); - for (i = 0; i < k; ++i) { - funcs[i] = func->funcs[i]->copy(); - } - bounds = (double *)gmallocn(k + 1, sizeof(double)); - memcpy(bounds, func->bounds, (k + 1) * sizeof(double)); - encode = (double *)gmallocn(2 * k, sizeof(double)); - memcpy(encode, func->encode, 2 * k * sizeof(double)); - scale = (double *)gmallocn(k, sizeof(double)); - memcpy(scale, func->scale, k * sizeof(double)); - ok = gTrue; -} - -StitchingFunction::~StitchingFunction() { - int i; - - if (funcs) { - for (i = 0; i < k; ++i) { - if (funcs[i]) { - delete funcs[i]; - } - } - } - gfree(funcs); - gfree(bounds); - gfree(encode); - gfree(scale); -} - -void StitchingFunction::transform(double *in, double *out) { - double x; - int i; - - if (in[0] < domain[0][0]) { - x = domain[0][0]; - } else if (in[0] > domain[0][1]) { - x = domain[0][1]; - } else { - x = in[0]; - } - for (i = 0; i < k - 1; ++i) { - if (x < bounds[i+1]) { - break; - } - } - x = encode[2*i] + (x - bounds[i]) * scale[i]; - funcs[i]->transform(&x, out); -} - -//------------------------------------------------------------------------ -// PostScriptFunction -//------------------------------------------------------------------------ - -// This is not an enum, because we can't foreward-declare the enum -// type in Function.h -// -// NB: This must be kept in sync with psOpNames[] below. -#define psOpAbs 0 -#define psOpAdd 1 -#define psOpAnd 2 -#define psOpAtan 3 -#define psOpBitshift 4 -#define psOpCeiling 5 -#define psOpCopy 6 -#define psOpCos 7 -#define psOpCvi 8 -#define psOpCvr 9 -#define psOpDiv 10 -#define psOpDup 11 -#define psOpEq 12 -#define psOpExch 13 -#define psOpExp 14 -#define psOpFalse 15 -#define psOpFloor 16 -#define psOpGe 17 -#define psOpGt 18 -#define psOpIdiv 19 -#define psOpIndex 20 -#define psOpLe 21 -#define psOpLn 22 -#define psOpLog 23 -#define psOpLt 24 -#define psOpMod 25 -#define psOpMul 26 -#define psOpNe 27 -#define psOpNeg 28 -#define psOpNot 29 -#define psOpOr 30 -#define psOpPop 31 -#define psOpRoll 32 -#define psOpRound 33 -#define psOpSin 34 -#define psOpSqrt 35 -#define psOpSub 36 -#define psOpTrue 37 -#define psOpTruncate 38 -#define psOpXor 39 -// the push/j/jz ops are used internally (and are not listed in psOpNames[]) -#define psOpPush 40 -#define psOpJ 41 -#define psOpJz 42 - -#define nPSOps (sizeof(psOpNames) / sizeof(const char *)) - -// Note: 'if' and 'ifelse' are parsed separately. -// The rest are listed here in alphabetical order. -// -// NB: This must be kept in sync with the psOpXXX defines above. -static const char *psOpNames[] = { - "abs", - "add", - "and", - "atan", - "bitshift", - "ceiling", - "copy", - "cos", - "cvi", - "cvr", - "div", - "dup", - "eq", - "exch", - "exp", - "false", - "floor", - "ge", - "gt", - "idiv", - "index", - "le", - "ln", - "log", - "lt", - "mod", - "mul", - "ne", - "neg", - "not", - "or", - "pop", - "roll", - "round", - "sin", - "sqrt", - "sub", - "true", - "truncate", - "xor" -}; - -struct PSCode { - int op; - union { - double d; - int i; - } val; -}; - -#define psStackSize 100 - -PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { - Stream *str; - GList *tokens; - GString *tok; - double in[funcMaxInputs]; - int tokPtr, codePtr, i; - - codeString = NULL; - code = NULL; - codeSize = 0; - ok = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (!hasRange) { - error(errSyntaxError, -1, "Type 4 function is missing range"); - goto err1; - } - - //----- get the stream - if (!funcObj->isStream()) { - error(errSyntaxError, -1, "Type 4 function isn't a stream"); - goto err1; - } - str = funcObj->getStream(); - - //----- tokenize the function - codeString = new GString(); - tokens = new GList(); - str->reset(); - while ((tok = getToken(str))) { - tokens->append(tok); - } - str->close(); - - //----- parse the function - if (tokens->getLength() < 1 || - ((GString *)tokens->get(0))->cmp("{")) { - error(errSyntaxError, -1, "Expected '{{' at start of PostScript function"); - goto err2; - } - tokPtr = 1; - codePtr = 0; - if (!parseCode(tokens, &tokPtr, &codePtr)) { - goto err2; - } - codeLen = codePtr; - - //----- set up the cache - for (i = 0; i < m; ++i) { - in[i] = domain[i][0]; - cacheIn[i] = in[i] - 1; - } - transform(in, cacheOut); - - ok = gTrue; - - err2: - deleteGList(tokens, GString); - err1: - return; -} - -PostScriptFunction::PostScriptFunction(PostScriptFunction *func) { - memcpy((void *)this, (void *)func, sizeof(PostScriptFunction)); - codeString = func->codeString->copy(); - code = (PSCode *)gmallocn(codeSize, sizeof(PSCode)); - memcpy(code, func->code, codeSize * sizeof(PSCode)); -} - -PostScriptFunction::~PostScriptFunction() { - gfree(code); - if (codeString) { - delete codeString; - } -} - -void PostScriptFunction::transform(double *in, double *out) { - double stack[psStackSize]; - double x; - int sp, i; - - // check the cache - for (i = 0; i < m; ++i) { - if (in[i] != cacheIn[i]) { - break; - } - } - if (i == m) { - for (i = 0; i < n; ++i) { - out[i] = cacheOut[i]; - } - return; - } - - for (i = 0; i < m; ++i) { - stack[psStackSize - 1 - i] = in[i]; - } - sp = exec(stack, psStackSize - m); - // if (sp < psStackSize - n) { - // error(errSyntaxWarning, -1, - // "Extra values on stack at end of PostScript function"); - // } - if (sp > psStackSize - n) { - error(errSyntaxError, -1, "Stack underflow in PostScript function"); - sp = psStackSize - n; - } - for (i = 0; i < n; ++i) { - x = stack[sp + n - 1 - i]; - if (x < range[i][0]) { - out[i] = range[i][0]; - } else if (x > range[i][1]) { - out[i] = range[i][1]; - } else { - out[i] = x; - } - } - - // save current result in the cache - for (i = 0; i < m; ++i) { - cacheIn[i] = in[i]; - } - for (i = 0; i < n; ++i) { - cacheOut[i] = out[i]; - } -} - -GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) { - GString *tok; - char *p; - int a, b, mid, cmp; - int codePtr0, codePtr1; - - while (1) { - if (*tokPtr >= tokens->getLength()) { - error(errSyntaxError, -1, - "Unexpected end of PostScript function stream"); - return gFalse; - } - tok = (GString *)tokens->get((*tokPtr)++); - p = tok->getCString(); - if (isdigit(*p) || *p == '.' || *p == '-') { - addCodeD(codePtr, psOpPush, atof(tok->getCString())); - } else if (!tok->cmp("{")) { - codePtr0 = *codePtr; - addCodeI(codePtr, psOpJz, 0); - if (!parseCode(tokens, tokPtr, codePtr)) { - return gFalse; - } - if (*tokPtr >= tokens->getLength()) { - error(errSyntaxError, -1, - "Unexpected end of PostScript function stream"); - return gFalse; - } - tok = (GString *)tokens->get((*tokPtr)++); - if (!tok->cmp("if")) { - code[codePtr0].val.i = *codePtr; - } else if (!tok->cmp("{")) { - codePtr1 = *codePtr; - addCodeI(codePtr, psOpJ, 0); - code[codePtr0].val.i = *codePtr; - if (!parseCode(tokens, tokPtr, codePtr)) { - return gFalse; - } - if (*tokPtr >= tokens->getLength()) { - error(errSyntaxError, -1, - "Unexpected end of PostScript function stream"); - return gFalse; - } - tok = (GString *)tokens->get((*tokPtr)++); - if (!tok->cmp("ifelse")) { - code[codePtr1].val.i = *codePtr; - } else { - error(errSyntaxError, -1, - "Expected 'ifelse' in PostScript function stream"); - return gFalse; - } - } else { - error(errSyntaxError, -1, - "Expected 'if' in PostScript function stream"); - return gFalse; - } - } else if (!tok->cmp("}")) { - break; - } else if (!tok->cmp("if")) { - error(errSyntaxError, -1, - "Unexpected 'if' in PostScript function stream"); - return gFalse; - } else if (!tok->cmp("ifelse")) { - error(errSyntaxError, -1, - "Unexpected 'ifelse' in PostScript function stream"); - return gFalse; - } else { - a = -1; - b = nPSOps; - cmp = 0; // make gcc happy - // invariant: psOpNames[a] < tok < psOpNames[b] - while (b - a > 1) { - mid = (a + b) / 2; - cmp = tok->cmp(psOpNames[mid]); - if (cmp > 0) { - a = mid; - } else if (cmp < 0) { - b = mid; - } else { - a = b = mid; - } - } - if (cmp != 0) { - error(errSyntaxError, -1, - "Unknown operator '{0:t}' in PostScript function", - tok); - return gFalse; - } - addCode(codePtr, a); - } - } - return gTrue; -} - -void PostScriptFunction::addCode(int *codePtr, int op) { - if (*codePtr >= codeSize) { - if (codeSize) { - codeSize *= 2; - } else { - codeSize = 16; - } - code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); - } - code[*codePtr].op = op; - ++(*codePtr); -} - -void PostScriptFunction::addCodeI(int *codePtr, int op, int x) { - if (*codePtr >= codeSize) { - if (codeSize) { - codeSize *= 2; - } else { - codeSize = 16; - } - code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); - } - code[*codePtr].op = op; - code[*codePtr].val.i = x; - ++(*codePtr); -} - -void PostScriptFunction::addCodeD(int *codePtr, int op, double x) { - if (*codePtr >= codeSize) { - if (codeSize) { - codeSize *= 2; - } else { - codeSize = 16; - } - code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); - } - code[*codePtr].op = op; - code[*codePtr].val.d = x; - ++(*codePtr); -} - -GString *PostScriptFunction::getToken(Stream *str) { - GString *s; - int c; - GBool comment; - - s = new GString(); - comment = gFalse; - while (1) { - if ((c = str->getChar()) == EOF) { - delete s; - return NULL; - } - codeString->append((char)c); - if (comment) { - if (c == '\x0a' || c == '\x0d') { - comment = gFalse; - } - } else if (c == '%') { - comment = gTrue; - } else if (!isspace(c)) { - break; - } - } - if (c == '{' || c == '}') { - s->append((char)c); - } else if (isdigit(c) || c == '.' || c == '-') { - while (1) { - s->append((char)c); - c = str->lookChar(); - if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { - break; - } - str->getChar(); - codeString->append((char)c); - } - } else { - while (1) { - s->append((char)c); - c = str->lookChar(); - if (c == EOF || !isalnum(c)) { - break; - } - str->getChar(); - codeString->append((char)c); - } - } - return s; -} - -int PostScriptFunction::exec(double *stack, int sp0) { - PSCode *c; - double tmp[psStackSize]; - double t; - int sp, ip, nn, k, i; - - sp = sp0; - ip = 0; - while (ip < codeLen) { - c = &code[ip++]; - switch(c->op) { - case psOpAbs: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = fabs(stack[sp]); - break; - case psOpAdd: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] + stack[sp]; - ++sp; - break; - case psOpAnd: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp]; - ++sp; - break; - case psOpAtan: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = atan2(stack[sp + 1], stack[sp]); - ++sp; - break; - case psOpBitshift: - if (sp + 1 >= psStackSize) { - goto underflow; - } - k = (int)stack[sp + 1]; - nn = (int)stack[sp]; - if (nn > 0) { - stack[sp + 1] = k << nn; - } else if (nn < 0) { - stack[sp + 1] = k >> -nn; - } else { - stack[sp + 1] = k; - } - ++sp; - break; - case psOpCeiling: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = ceil(stack[sp]); - break; - case psOpCopy: - if (sp >= psStackSize) { - goto underflow; - } - nn = (int)stack[sp++]; - if (nn < 0) { - goto invalidArg; - } - if (sp + nn > psStackSize) { - goto underflow; - } - if (sp - nn < 0) { - goto overflow; - } - for (i = 0; i < nn; ++i) { - stack[sp - nn + i] = stack[sp + i]; - } - sp -= nn; - break; - case psOpCos: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = cos(stack[sp]); - break; - case psOpCvi: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = (int)stack[sp]; - break; - case psOpCvr: - if (sp >= psStackSize) { - goto underflow; - } - break; - case psOpDiv: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] / stack[sp]; - ++sp; - break; - case psOpDup: - if (sp >= psStackSize) { - goto underflow; - } - if (sp < 1) { - goto overflow; - } - stack[sp - 1] = stack[sp]; - --sp; - break; - case psOpEq: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0; - ++sp; - break; - case psOpExch: - if (sp + 1 >= psStackSize) { - goto underflow; - } - t = stack[sp]; - stack[sp] = stack[sp + 1]; - stack[sp + 1] = t; - break; - case psOpExp: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = pow(stack[sp + 1], stack[sp]); - ++sp; - break; - case psOpFalse: - if (sp < 1) { - goto overflow; - } - stack[sp - 1] = 0; - --sp; - break; - case psOpFloor: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = floor(stack[sp]); - break; - case psOpGe: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0; - ++sp; - break; - case psOpGt: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0; - ++sp; - break; - case psOpIdiv: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp]; - ++sp; - break; - case psOpIndex: - if (sp >= psStackSize) { - goto underflow; - } - k = (int)stack[sp]; - if (k < 0) { - goto invalidArg; - } - if (sp + 1 + k >= psStackSize) { - goto underflow; - } - stack[sp] = stack[sp + 1 + k]; - break; - case psOpLe: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0; - ++sp; - break; - case psOpLn: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = log(stack[sp]); - break; - case psOpLog: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = log10(stack[sp]); - break; - case psOpLt: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0; - ++sp; - break; - case psOpMod: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp]; - ++sp; - break; - case psOpMul: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] * stack[sp]; - ++sp; - break; - case psOpNe: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0; - ++sp; - break; - case psOpNeg: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = -stack[sp]; - break; - case psOpNot: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = stack[sp] == 0 ? 1 : 0; - break; - case psOpOr: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp]; - ++sp; - break; - case psOpPop: - if (sp >= psStackSize) { - goto underflow; - } - ++sp; - break; - case psOpRoll: - if (sp + 1 >= psStackSize) { - goto underflow; - } - k = (int)stack[sp++]; - nn = (int)stack[sp++]; - if (nn < 0) { - goto invalidArg; - } - if (sp + nn > psStackSize) { - goto underflow; - } - if (k >= 0) { - k %= nn; - } else { - k = -k % nn; - if (k) { - k = nn - k; - } - } - for (i = 0; i < nn; ++i) { - tmp[i] = stack[sp + i]; - } - for (i = 0; i < nn; ++i) { - stack[sp + i] = tmp[(i + k) % nn]; - } - break; - case psOpRound: - if (sp >= psStackSize) { - goto underflow; - } - t = stack[sp]; - stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5); - break; - case psOpSin: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = sin(stack[sp]); - break; - case psOpSqrt: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = sqrt(stack[sp]); - break; - case psOpSub: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] - stack[sp]; - ++sp; - break; - case psOpTrue: - if (sp < 1) { - goto overflow; - } - stack[sp - 1] = 1; - --sp; - break; - case psOpTruncate: - if (sp >= psStackSize) { - goto underflow; - } - t = stack[sp]; - stack[sp] = (t >= 0) ? floor(t) : ceil(t); - break; - case psOpXor: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp]; - ++sp; - break; - case psOpPush: - if (sp < 1) { - goto overflow; - } - stack[--sp] = c->val.d; - break; - case psOpJ: - ip = c->val.i; - break; - case psOpJz: - if (sp >= psStackSize) { - goto underflow; - } - k = (int)stack[sp++]; - if (k == 0) { - ip = c->val.i; - } - break; - } - } - return sp; - - underflow: - error(errSyntaxError, -1, "Stack underflow in PostScript function"); - return sp; - overflow: - error(errSyntaxError, -1, "Stack overflow in PostScript function"); - return sp; - invalidArg: - error(errSyntaxError, -1, "Invalid arg in PostScript function"); - return sp; -} diff --git a/test/bug-hunting/cve/CVE-2019-10023/Function.h b/test/bug-hunting/cve/CVE-2019-10023/Function.h deleted file mode 100644 index 615c2abfddf..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10023/Function.h +++ /dev/null @@ -1,310 +0,0 @@ -//======================================================================== -// -// Function.h -// -// Copyright 2001-2003 Glyph & Cog, LLC -// -//======================================================================== - -#ifndef FUNCTION_H -#define FUNCTION_H - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "gtypes.h" -#include "Object.h" - -class GList; -class Dict; -class Stream; -struct PSCode; - -//------------------------------------------------------------------------ -// Function -//------------------------------------------------------------------------ - -#define funcMaxInputs 32 -#define funcMaxOutputs 32 -#define sampledFuncMaxInputs 16 - -class Function { -public: - - Function(); - - virtual ~Function(); - - // Construct a function. Returns NULL if unsuccessful. - static Function *parse(Object *funcObj, int recursion = 0); - - // Initialize the entries common to all function types. - GBool init(Dict *dict); - - virtual Function *copy() = 0; - - // Return the function type: - // -1 : identity - // 0 : sampled - // 2 : exponential - // 3 : stitching - // 4 : PostScript - virtual int getType() = 0; - - // Return size of input and output tuples. - int getInputSize() { - return m; - } - int getOutputSize() { - return n; - } - - double getDomainMin(int i) { - return domain[i][0]; - } - double getDomainMax(int i) { - return domain[i][1]; - } - double getRangeMin(int i) { - return range[i][0]; - } - double getRangeMax(int i) { - return range[i][1]; - } - GBool getHasRange() { - return hasRange; - } - - // Transform an input tuple into an output tuple. - virtual void transform(double *in, double *out) = 0; - - virtual GBool isOk() = 0; - -protected: - - int m, n; // size of input and output tuples - double // min and max values for function domain - domain[funcMaxInputs][2]; - double // min and max values for function range - range[funcMaxOutputs][2]; - GBool hasRange; // set if range is defined -}; - -//------------------------------------------------------------------------ -// IdentityFunction -//------------------------------------------------------------------------ - -class IdentityFunction : public Function { -public: - - IdentityFunction(); - virtual ~IdentityFunction(); - virtual Function *copy() { - return new IdentityFunction(); - } - virtual int getType() { - return -1; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return gTrue; - } - -private: -}; - -//------------------------------------------------------------------------ -// SampledFunction -//------------------------------------------------------------------------ - -class SampledFunction : public Function { -public: - - SampledFunction(Object *funcObj, Dict *dict); - virtual ~SampledFunction(); - virtual Function *copy() { - return new SampledFunction(this); - } - virtual int getType() { - return 0; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - int getSampleSize(int i) { - return sampleSize[i]; - } - double getEncodeMin(int i) { - return encode[i][0]; - } - double getEncodeMax(int i) { - return encode[i][1]; - } - double getDecodeMin(int i) { - return decode[i][0]; - } - double getDecodeMax(int i) { - return decode[i][1]; - } - double *getSamples() { - return samples; - } - -private: - - SampledFunction(SampledFunction *func); - - int // number of samples for each domain element - sampleSize[funcMaxInputs]; - double // min and max values for domain encoder - encode[funcMaxInputs][2]; - double // min and max values for range decoder - decode[funcMaxOutputs][2]; - double // input multipliers - inputMul[funcMaxInputs]; - int *idxOffset; - double *samples; // the samples - int nSamples; // size of the samples array - double *sBuf; // buffer for the transform function - double cacheIn[funcMaxInputs]; - double cacheOut[funcMaxOutputs]; - GBool ok; -}; - -//------------------------------------------------------------------------ -// ExponentialFunction -//------------------------------------------------------------------------ - -class ExponentialFunction : public Function { -public: - - ExponentialFunction(Object *funcObj, Dict *dict); - virtual ~ExponentialFunction(); - virtual Function *copy() { - return new ExponentialFunction(this); - } - virtual int getType() { - return 2; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - double *getC0() { - return c0; - } - double *getC1() { - return c1; - } - double getE() { - return e; - } - -private: - - ExponentialFunction(ExponentialFunction *func); - - double c0[funcMaxOutputs]; - double c1[funcMaxOutputs]; - double e; - GBool ok; -}; - -//------------------------------------------------------------------------ -// StitchingFunction -//------------------------------------------------------------------------ - -class StitchingFunction : public Function { -public: - - StitchingFunction(Object *funcObj, Dict *dict, int recursion); - virtual ~StitchingFunction(); - virtual Function *copy() { - return new StitchingFunction(this); - } - virtual int getType() { - return 3; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - int getNumFuncs() { - return k; - } - Function *getFunc(int i) { - return funcs[i]; - } - double *getBounds() { - return bounds; - } - double *getEncode() { - return encode; - } - double *getScale() { - return scale; - } - -private: - - StitchingFunction(StitchingFunction *func); - - int k; - Function **funcs; - double *bounds; - double *encode; - double *scale; - GBool ok; -}; - -//------------------------------------------------------------------------ -// PostScriptFunction -//------------------------------------------------------------------------ - -class PostScriptFunction : public Function { -public: - - PostScriptFunction(Object *funcObj, Dict *dict); - virtual ~PostScriptFunction(); - virtual Function *copy() { - return new PostScriptFunction(this); - } - virtual int getType() { - return 4; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - GString *getCodeString() { - return codeString; - } - -private: - - PostScriptFunction(PostScriptFunction *func); - GBool parseCode(GList *tokens, int *tokPtr, int *codePtr); - void addCode(int *codePtr, int op); - void addCodeI(int *codePtr, int op, int x); - void addCodeD(int *codePtr, int op, double x); - GString *getToken(Stream *str); - int exec(double *stack, int sp0); - - GString *codeString; - PSCode *code; - int codeLen; - int codeSize; - double cacheIn[funcMaxInputs]; - double cacheOut[funcMaxOutputs]; - GBool ok; -}; - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-10023/expected.txt b/test/bug-hunting/cve/CVE-2019-10023/expected.txt deleted file mode 100644 index 5866a9db212..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10023/expected.txt +++ /dev/null @@ -1 +0,0 @@ -Function.cc:1420:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-10024/Splash.cc b/test/bug-hunting/cve/CVE-2019-10024/Splash.cc deleted file mode 100644 index 77718abbe70..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10024/Splash.cc +++ /dev/null @@ -1,7184 +0,0 @@ -//======================================================================== -// -// Splash.cc -// -// Copyright 2003-2013 Glyph & Cog, LLC -// -//======================================================================== - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include -#include -#include -#include -#include "gmem.h" -#include "gmempp.h" -#include "SplashErrorCodes.h" -#include "SplashMath.h" -#include "SplashBitmap.h" -#include "SplashState.h" -#include "SplashPath.h" -#include "SplashXPath.h" -#include "SplashXPathScanner.h" -#include "SplashPattern.h" -#include "SplashScreen.h" -#include "SplashFont.h" -#include "SplashGlyphBitmap.h" -#include "Splash.h" - -// the MSVC math.h doesn't define this -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -//------------------------------------------------------------------------ - -// distance of Bezier control point from center for circle approximation -// = (4 * (sqrt(2) - 1) / 3) * r -#define bezierCircle ((SplashCoord)0.55228475) -#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475)) - -// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. -static inline Guchar div255(int x) { - return (Guchar)((x + (x >> 8) + 0x80) >> 8); -} - -// Clip x to lie in [0, 255]. -static inline Guchar clip255(int x) { - return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x; -} - -// Used by drawImage and fillImageMask to divide the target -// quadrilateral into sections. -struct ImageSection { - int y0, y1; // actual y range - int ia0, ia1; // vertex indices for edge A - int ib0, ib1; // vertex indices for edge B - SplashCoord xa0, ya0, xa1, ya1; // edge A - SplashCoord dxdya; // slope of edge A - SplashCoord xb0, yb0, xb1, yb1; // edge B - SplashCoord dxdyb; // slope of edge B -}; - -//------------------------------------------------------------------------ -// SplashPipe -//------------------------------------------------------------------------ - -#define splashPipeMaxStages 9 - -struct SplashPipe { - // source pattern - SplashPattern *pattern; - - // source alpha and color - Guchar aInput; - SplashColor cSrcVal; - - // special cases and result color - GBool noTransparency; - GBool shapeOnly; - SplashPipeResultColorCtrl resultColorCtrl; - - // non-isolated group correction - // (this is only used when Splash::composite() is called to composite - // a non-isolated group onto the backdrop) - GBool nonIsolatedGroup; - - // the "run" function - void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -}; - -SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { - splashPipeResultColorNoAlphaBlendMono, - splashPipeResultColorNoAlphaBlendMono, - splashPipeResultColorNoAlphaBlendRGB, - splashPipeResultColorNoAlphaBlendRGB -#if SPLASH_CMYK - , - splashPipeResultColorNoAlphaBlendCMYK -#endif -}; - -SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { - splashPipeResultColorAlphaNoBlendMono, - splashPipeResultColorAlphaNoBlendMono, - splashPipeResultColorAlphaNoBlendRGB, - splashPipeResultColorAlphaNoBlendRGB -#if SPLASH_CMYK - , - splashPipeResultColorAlphaNoBlendCMYK -#endif -}; - -SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { - splashPipeResultColorAlphaBlendMono, - splashPipeResultColorAlphaBlendMono, - splashPipeResultColorAlphaBlendRGB, - splashPipeResultColorAlphaBlendRGB -#if SPLASH_CMYK - , - splashPipeResultColorAlphaBlendCMYK -#endif -}; - -//------------------------------------------------------------------------ -// modified region -//------------------------------------------------------------------------ - -void Splash::clearModRegion() { - modXMin = bitmap->width; - modYMin = bitmap->height; - modXMax = -1; - modYMax = -1; -} - -inline void Splash::updateModX(int x) { - if (x < modXMin) { - modXMin = x; - } - if (x > modXMax) { - modXMax = x; - } -} - -inline void Splash::updateModY(int y) { - if (y < modYMin) { - modYMin = y; - } - if (y > modYMax) { - modYMax = y; - } -} - -//------------------------------------------------------------------------ -// pipeline -//------------------------------------------------------------------------ - -inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern, - Guchar aInput, GBool usesShape, - GBool nonIsolatedGroup) { - SplashColorMode mode; - - mode = bitmap->mode; - - pipe->pattern = NULL; - - // source color - if (pattern && pattern->isStatic()) { - pattern->getColor(0, 0, pipe->cSrcVal); - pipe->pattern = NULL; - } else { - pipe->pattern = pattern; - } - - // source alpha - pipe->aInput = aInput; - - // special cases - pipe->noTransparency = aInput == 255 && - !state->softMask && - !usesShape && - !state->inNonIsolatedGroup && - !state->inKnockoutGroup && - !nonIsolatedGroup && - state->overprintMask == 0xffffffff; - pipe->shapeOnly = aInput == 255 && - !state->softMask && - usesShape && - !state->inNonIsolatedGroup && - !state->inKnockoutGroup && - !nonIsolatedGroup && - state->overprintMask == 0xffffffff; - - // result color - if (pipe->noTransparency) { - // the !state->blendFunc case is handled separately in pipeRun - pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[mode]; - } else if (!state->blendFunc) { - pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[mode]; - } else { - pipe->resultColorCtrl = pipeResultColorAlphaBlend[mode]; - } - - // non-isolated group correction - pipe->nonIsolatedGroup = nonIsolatedGroup; - - // select the 'run' function - pipe->run = &Splash::pipeRun; - if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) { - if (mode == splashModeMono1 && !bitmap->alpha) { - pipe->run = &Splash::pipeRunSimpleMono1; - } else if (mode == splashModeMono8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunSimpleMono8; - } else if (mode == splashModeRGB8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunSimpleRGB8; - } else if (mode == splashModeBGR8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunSimpleBGR8; -#if SPLASH_CMYK - } else if (mode == splashModeCMYK8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunSimpleCMYK8; -#endif - } - } else if (!pipe->pattern && pipe->shapeOnly && !state->blendFunc) { - if (mode == splashModeMono1 && !bitmap->alpha) { - pipe->run = &Splash::pipeRunShapeMono1; - } else if (mode == splashModeMono8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunShapeMono8; - } else if (mode == splashModeRGB8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunShapeRGB8; - } else if (mode == splashModeBGR8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunShapeBGR8; -#if SPLASH_CMYK - } else if (mode == splashModeCMYK8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunShapeCMYK8; -#endif - } - } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask && - usesShape && - !(state->inNonIsolatedGroup && groupBackBitmap->alpha) && - !state->inKnockoutGroup && - !state->blendFunc && !pipe->nonIsolatedGroup) { - if (mode == splashModeMono1 && !bitmap->alpha) { - pipe->run = &Splash::pipeRunAAMono1; - } else if (mode == splashModeMono8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunAAMono8; - } else if (mode == splashModeRGB8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunAARGB8; - } else if (mode == splashModeBGR8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunAABGR8; -#if SPLASH_CMYK - } else if (mode == splashModeCMYK8 && bitmap->alpha) { - pipe->run = &Splash::pipeRunAACMYK8; -#endif - } - } -} - -// general case -void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar *shapePtr2; - Guchar shape, aSrc, aDest, alphaI, alphaIm1, alpha0, aResult; - SplashColor cSrc, cDest, cBlend; - Guchar shapeVal, cResult0, cResult1, cResult2, cResult3; - int cSrcStride, shapeStride, x, lastX, t; - SplashColorPtr destColorPtr; - Guchar destColorMask; - Guchar *destAlphaPtr; - SplashColorPtr color0Ptr; - Guchar color0Mask; - Guchar *alpha0Ptr; - SplashColorPtr softMaskPtr; -#if SPLASH_CMYK - SplashColor cSrc2, cDest2; -#endif - - if (cSrcPtr && !pipe->pattern) { - cSrcStride = bitmapComps; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - - if (shapePtr) { - shapePtr2 = shapePtr; - shapeStride = 1; - for (; x0 <= x1; ++x0) { - if (*shapePtr2) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr2; - } - } else { - shapeVal = 0xff; - shapePtr2 = &shapeVal; - shapeStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - if (bitmap->mode == splashModeMono1) { - destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - destColorMask = (Guchar)(0x80 >> (x0 & 7)); - } else { - destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps]; - destColorMask = 0; // make gcc happy - } - if (bitmap->alpha) { - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - } else { - destAlphaPtr = NULL; - } - if (state->softMask) { - softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0]; - } else { - softMaskPtr = NULL; - } - if (state->inKnockoutGroup) { - if (bitmap->mode == splashModeMono1) { - color0Ptr = - &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + - ((groupBackX + x0) >> 3)]; - color0Mask = (Guchar)(0x80 >> ((groupBackX + x0) & 7)); - } else { - color0Ptr = - &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + - (groupBackX + x0) * bitmapComps]; - color0Mask = 0; // make gcc happy - } - } else { - color0Ptr = NULL; - color0Mask = 0; // make gcc happy - } - if (state->inNonIsolatedGroup && groupBackBitmap->alpha) { - alpha0Ptr = - &groupBackBitmap->alpha[(groupBackY + y) - * groupBackBitmap->alphaRowSize + - (groupBackX + x0)]; - } else { - alpha0Ptr = NULL; - } - - for (x = x0; x <= x1; ++x) { - - //----- shape - - shape = *shapePtr2; - if (!shape) { - if (bitmap->mode == splashModeMono1) { - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - } else { - destColorPtr += bitmapComps; - } - if (destAlphaPtr) { - ++destAlphaPtr; - } - if (softMaskPtr) { - ++softMaskPtr; - } - if (color0Ptr) { - if (bitmap->mode == splashModeMono1) { - color0Ptr += color0Mask & 1; - color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1)); - } else { - color0Ptr += bitmapComps; - } - } - if (alpha0Ptr) { - ++alpha0Ptr; - } - cSrcPtr += cSrcStride; - shapePtr2 += shapeStride; - continue; - } - lastX = x; - - //----- source color - - // static pattern: handled in pipeInit - // fixed color: handled in pipeInit - - // dynamic pattern - if (pipe->pattern) { - pipe->pattern->getColor(x, y, pipe->cSrcVal); - } - - cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy - - if (pipe->noTransparency && !state->blendFunc) { - - //----- result color - - switch (bitmap->mode) { - case splashModeMono1: - case splashModeMono8: - cResult0 = state->grayTransfer[cSrcPtr[0]]; - break; - case splashModeRGB8: - case splashModeBGR8: - cResult0 = state->rgbTransferR[cSrcPtr[0]]; - cResult1 = state->rgbTransferG[cSrcPtr[1]]; - cResult2 = state->rgbTransferB[cSrcPtr[2]]; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - cResult0 = state->cmykTransferC[cSrcPtr[0]]; - cResult1 = state->cmykTransferM[cSrcPtr[1]]; - cResult2 = state->cmykTransferY[cSrcPtr[2]]; - cResult3 = state->cmykTransferK[cSrcPtr[3]]; - break; -#endif - } - aResult = 255; - - } else { // if (noTransparency && !blendFunc) - - //----- read destination pixel - // (or backdrop color, for knockout groups) - - if (color0Ptr) { - - switch (bitmap->mode) { - case splashModeMono1: - cDest[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00; - color0Ptr += color0Mask & 1; - color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1)); - break; - case splashModeMono8: - cDest[0] = *color0Ptr++; - break; - case splashModeRGB8: - cDest[0] = color0Ptr[0]; - cDest[1] = color0Ptr[1]; - cDest[2] = color0Ptr[2]; - color0Ptr += 3; - break; - case splashModeBGR8: - cDest[2] = color0Ptr[0]; - cDest[1] = color0Ptr[1]; - cDest[0] = color0Ptr[2]; - color0Ptr += 3; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - cDest[0] = color0Ptr[0]; - cDest[1] = color0Ptr[1]; - cDest[2] = color0Ptr[2]; - cDest[3] = color0Ptr[3]; - color0Ptr += 4; - break; -#endif - } - - } else { - - switch (bitmap->mode) { - case splashModeMono1: - cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00; - break; - case splashModeMono8: - cDest[0] = *destColorPtr; - break; - case splashModeRGB8: - cDest[0] = destColorPtr[0]; - cDest[1] = destColorPtr[1]; - cDest[2] = destColorPtr[2]; - break; - case splashModeBGR8: - cDest[0] = destColorPtr[2]; - cDest[1] = destColorPtr[1]; - cDest[2] = destColorPtr[0]; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - cDest[0] = destColorPtr[0]; - cDest[1] = destColorPtr[1]; - cDest[2] = destColorPtr[2]; - cDest[3] = destColorPtr[3]; - break; -#endif - } - - } - - if (destAlphaPtr) { - aDest = *destAlphaPtr; - } else { - aDest = 0xff; - } - - //----- read source color; handle overprint - - switch (bitmap->mode) { - case splashModeMono1: - case splashModeMono8: - cSrc[0] = state->grayTransfer[cSrcPtr[0]]; - break; - case splashModeRGB8: - case splashModeBGR8: - cSrc[0] = state->rgbTransferR[cSrcPtr[0]]; - cSrc[1] = state->rgbTransferG[cSrcPtr[1]]; - cSrc[2] = state->rgbTransferB[cSrcPtr[2]]; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - if (state->overprintMask & 0x01) { - cSrc[0] = state->cmykTransferC[cSrcPtr[0]]; - } else { - cSrc[0] = div255(aDest * cDest[0]); - } - if (state->overprintMask & 0x02) { - cSrc[1] = state->cmykTransferM[cSrcPtr[1]]; - } else { - cSrc[1] = div255(aDest * cDest[1]); - } - if (state->overprintMask & 0x04) { - cSrc[2] = state->cmykTransferY[cSrcPtr[2]]; - } else { - cSrc[2] = div255(aDest * cDest[2]); - } - if (state->overprintMask & 0x08) { - cSrc[3] = state->cmykTransferK[cSrcPtr[3]]; - } else { - cSrc[3] = div255(aDest * cDest[3]); - } - break; -#endif - } - - //----- source alpha - - if (softMaskPtr) { - if (shapePtr) { - aSrc = div255(div255(pipe->aInput * *softMaskPtr++) * shape); - } else { - aSrc = div255(pipe->aInput * *softMaskPtr++); - } - } else if (shapePtr) { - aSrc = div255(pipe->aInput * shape); - } else { - aSrc = pipe->aInput; - } - - //----- non-isolated group correction - - if (pipe->nonIsolatedGroup) { - // This path is only used when Splash::composite() is called to - // composite a non-isolated group onto the backdrop. In this - // case, shape is the source (group) alpha. - t = (aDest * 255) / shape - aDest; - switch (bitmap->mode) { -#if SPLASH_CMYK - case splashModeCMYK8: - cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255); -#endif - case splashModeRGB8: - case splashModeBGR8: - cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255); - cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255); - case splashModeMono1: - case splashModeMono8: - cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255); - break; - } - } - - //----- blend function - - if (state->blendFunc) { -#if SPLASH_CMYK - if (bitmap->mode == splashModeCMYK8) { - // convert colors to additive - cSrc2[0] = (Guchar)(0xff - cSrc[0]); - cSrc2[1] = (Guchar)(0xff - cSrc[1]); - cSrc2[2] = (Guchar)(0xff - cSrc[2]); - cSrc2[3] = (Guchar)(0xff - cSrc[3]); - cDest2[0] = (Guchar)(0xff - cDest[0]); - cDest2[1] = (Guchar)(0xff - cDest[1]); - cDest2[2] = (Guchar)(0xff - cDest[2]); - cDest2[3] = (Guchar)(0xff - cDest[3]); - (*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode); - // convert result back to subtractive - cBlend[0] = (Guchar)(0xff - cBlend[0]); - cBlend[1] = (Guchar)(0xff - cBlend[1]); - cBlend[2] = (Guchar)(0xff - cBlend[2]); - cBlend[3] = (Guchar)(0xff - cBlend[3]); - } else -#endif - (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode); - } - - //----- result alpha and non-isolated group element correction - - // alphaI = alpha_i - // alphaIm1 = alpha_(i-1) - - if (pipe->noTransparency) { - alphaI = alphaIm1 = aResult = 255; - } else if (alpha0Ptr) { - if (color0Ptr) { - // non-isolated, knockout - aResult = aSrc; - alpha0 = *alpha0Ptr++; - alphaI = (Guchar)(aSrc + alpha0 - div255(aSrc * alpha0)); - alphaIm1 = alpha0; - } else { - // non-isolated, non-knockout - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alpha0 = *alpha0Ptr++; - alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0)); - alphaIm1 = (Guchar)(alpha0 + aDest - div255(alpha0 * aDest)); - } - } else { - if (color0Ptr) { - // isolated, knockout - aResult = aSrc; - alphaI = aSrc; - alphaIm1 = 0; - } else { - // isolated, non-knockout - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - alphaIm1 = aDest; - } - } - - //----- result color - - switch (pipe->resultColorCtrl) { - - case splashPipeResultColorNoAlphaBlendMono: - cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); - break; - case splashPipeResultColorNoAlphaBlendRGB: - cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); - cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]); - cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]); - break; -#if SPLASH_CMYK - case splashPipeResultColorNoAlphaBlendCMYK: - cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); - cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]); - cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]); - cResult3 = div255((255 - aDest) * cSrc[3] + aDest * cBlend[3]); - break; -#endif - - case splashPipeResultColorAlphaNoBlendMono: - if (alphaI == 0) { - cResult0 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) - / alphaI); - } - break; - case splashPipeResultColorAlphaNoBlendRGB: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) - / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) - / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) - / alphaI); - } - break; -#if SPLASH_CMYK - case splashPipeResultColorAlphaNoBlendCMYK: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - cResult3 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) - / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) - / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) - / alphaI); - cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + aSrc * cSrc[3]) - / alphaI); - } - break; -#endif - - case splashPipeResultColorAlphaBlendMono: - if (alphaI == 0) { - cResult0 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + - aSrc * ((255 - alphaIm1) * cSrc[0] + - alphaIm1 * cBlend[0]) / 255) - / alphaI); - } - break; - case splashPipeResultColorAlphaBlendRGB: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + - aSrc * ((255 - alphaIm1) * cSrc[0] + - alphaIm1 * cBlend[0]) / 255) - / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + - aSrc * ((255 - alphaIm1) * cSrc[1] + - alphaIm1 * cBlend[1]) / 255) - / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + - aSrc * ((255 - alphaIm1) * cSrc[2] + - alphaIm1 * cBlend[2]) / 255) - / alphaI); - } - break; -#if SPLASH_CMYK - case splashPipeResultColorAlphaBlendCMYK: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - cResult3 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + - aSrc * ((255 - alphaIm1) * cSrc[0] + - alphaIm1 * cBlend[0]) / 255) - / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + - aSrc * ((255 - alphaIm1) * cSrc[1] + - alphaIm1 * cBlend[1]) / 255) - / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + - aSrc * ((255 - alphaIm1) * cSrc[2] + - alphaIm1 * cBlend[2]) / 255) - / alphaI); - cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + - aSrc * ((255 - alphaIm1) * cSrc[3] + - alphaIm1 * cBlend[3]) / 255) - / alphaI); - } - break; -#endif - } - - } // if (noTransparency && !blendFunc) - - //----- write destination pixel - - switch (bitmap->mode) { - case splashModeMono1: - if (state->screen->test(x, y, cResult0)) { - *destColorPtr |= destColorMask; - } else { - *destColorPtr &= (Guchar)~destColorMask; - } - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - break; - case splashModeMono8: - *destColorPtr++ = cResult0; - break; - case splashModeRGB8: - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr += 3; - break; - case splashModeBGR8: - destColorPtr[0] = cResult2; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult0; - destColorPtr += 3; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr[3] = cResult3; - destColorPtr += 4; - break; -#endif - } - if (destAlphaPtr) { - *destAlphaPtr++ = aResult; - } - - cSrcPtr += cSrcStride; - shapePtr2 += shapeStride; - } // for (x ...) - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeMono1 && !bitmap->alpha) { -void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar cResult0; - SplashColorPtr destColorPtr; - Guchar destColorMask; - SplashScreenCursor screenCursor; - int cSrcStride, x; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModX(x1); - updateModY(y); - - destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - destColorMask = (Guchar)(0x80 >> (x0 & 7)); - - screenCursor = state->screen->getTestCursor(y); - - for (x = x0; x <= x1; ++x) { - - //----- write destination pixel - cResult0 = state->grayTransfer[cSrcPtr[0]]; - if (state->screen->testWithCursor(screenCursor, x, cResult0)) { - *destColorPtr |= destColorMask; - } else { - *destColorPtr &= (Guchar)~destColorMask; - } - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - - cSrcPtr += cSrcStride; - } -} - -// special case: -// !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeMono8 && bitmap->alpha) { -void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModX(x1); - updateModY(y); - - destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- write destination pixel - *destColorPtr++ = state->grayTransfer[cSrcPtr[0]]; - *destAlphaPtr++ = 255; - - cSrcPtr += cSrcStride; - } -} - -// special case: -// !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeRGB8 && bitmap->alpha) { -void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModX(x1); - updateModY(y); - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- write destination pixel - destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]]; - destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; - destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]]; - destColorPtr += 3; - *destAlphaPtr++ = 255; - - cSrcPtr += cSrcStride; - } -} - -// special case: -// !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeBGR8 && bitmap->alpha) { -void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModX(x1); - updateModY(y); - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- write destination pixel - destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]]; - destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; - destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]]; - destColorPtr += 3; - *destAlphaPtr++ = 255; - - cSrcPtr += cSrcStride; - } -} - -#if SPLASH_CMYK -// special case: -// !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeCMYK8 && bitmap->alpha) { -void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x; - - if (cSrcPtr) { - cSrcStride = 4; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModX(x1); - updateModY(y); - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- write destination pixel - destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]]; - destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]]; - destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]]; - destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]]; - destColorPtr += 4; - *destAlphaPtr++ = 255; - - cSrcPtr += cSrcStride; - } -} -#endif - - -// special case: -// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && -// bitmap->mode == splashModeMono1 && !bitmap->alpha -void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, cSrc0, cDest0, cResult0; - SplashColorPtr destColorPtr; - Guchar destColorMask; - SplashScreenCursor screenCursor; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - destColorMask = (Guchar)(0x80 >> (x0 & 7)); - - screenCursor = state->screen->getTestCursor(y); - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- source color - cSrc0 = state->grayTransfer[cSrcPtr[0]]; - - //----- source alpha - aSrc = shape; - - //----- special case for aSrc = 255 - if (aSrc == 255) { - cResult0 = cSrc0; - } else { - - //----- read destination pixel - cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; - - //----- result color - // note: aDest = alphaI = aResult = 0xff - cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0); - } - - //----- write destination pixel - if (state->screen->testWithCursor(screenCursor, x, cResult0)) { - *destColorPtr |= destColorMask; - } else { - *destColorPtr &= (Guchar)~destColorMask; - } - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && -// bitmap->mode == splashModeMono8 && bitmap->alpha -void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - ++destColorPtr; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- source color - cSrc0 = state->grayTransfer[cSrcPtr[0]]; - - //----- source alpha - aSrc = shape; - - //----- special case for aSrc = 255 - if (aSrc == 255) { - aResult = 255; - cResult0 = cSrc0; - } else { - - //----- read destination alpha - aDest = *destAlphaPtr; - - //----- special case for aDest = 0 - if (aDest == 0) { - aResult = aSrc; - cResult0 = cSrc0; - } else { - - //----- read destination pixel - cDest0 = *destColorPtr; - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - } - } - - //----- write destination pixel - *destColorPtr++ = cResult0; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && -// bitmap->mode == splashModeRGB8 && bitmap->alpha -void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2; - Guchar cDest0, cDest1, cDest2; - Guchar cResult0, cResult1, cResult2; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 3; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- source color - cSrc0 = state->rgbTransferR[cSrcPtr[0]]; - cSrc1 = state->rgbTransferG[cSrcPtr[1]]; - cSrc2 = state->rgbTransferB[cSrcPtr[2]]; - - //----- source alpha - aSrc = shape; - - //----- special case for aSrc = 255 - if (aSrc == 255) { - aResult = 255; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - } else { - - //----- read destination alpha - aDest = *destAlphaPtr; - - //----- special case for aDest = 0 - if (aDest == 0) { - aResult = aSrc; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - } else { - - //----- read destination pixel - cDest0 = destColorPtr[0]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[2]; - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - } - } - - //----- write destination pixel - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr += 3; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && -// bitmap->mode == splashModeBGR8 && bitmap->alpha -void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2; - Guchar cDest0, cDest1, cDest2; - Guchar cResult0, cResult1, cResult2; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 3; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- source color - cSrc0 = state->rgbTransferR[cSrcPtr[0]]; - cSrc1 = state->rgbTransferG[cSrcPtr[1]]; - cSrc2 = state->rgbTransferB[cSrcPtr[2]]; - - //----- source alpha - aSrc = shape; - - //----- special case for aSrc = 255 - if (aSrc == 255) { - aResult = 255; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - } else { - - //----- read destination alpha - aDest = *destAlphaPtr; - - //----- special case for aDest = 0 - if (aDest == 0) { - aResult = aSrc; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - } else { - - //----- read destination pixel - cDest0 = destColorPtr[2]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[0]; - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - } - } - - //----- write destination pixel - destColorPtr[0] = cResult2; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult0; - destColorPtr += 3; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -#if SPLASH_CMYK -// special case: -// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && -// bitmap->mode == splashModeCMYK8 && bitmap->alpha -void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2, cSrc3; - Guchar cDest0, cDest1, cDest2, cDest3; - Guchar cResult0, cResult1, cResult2, cResult3; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 4; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 4; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = destColorPtr[0]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[2]; - cDest3 = destColorPtr[3]; - aDest = *destAlphaPtr; - - //----- overprint - if (state->overprintMask & 1) { - cSrc0 = state->cmykTransferC[cSrcPtr[0]]; - } else { - cSrc0 = div255(aDest * cDest0); - } - if (state->overprintMask & 2) { - cSrc1 = state->cmykTransferM[cSrcPtr[1]]; - } else { - cSrc1 = div255(aDest * cDest1); - } - if (state->overprintMask & 4) { - cSrc2 = state->cmykTransferY[cSrcPtr[2]]; - } else { - cSrc2 = div255(aDest * cDest2); - } - if (state->overprintMask & 8) { - cSrc3 = state->cmykTransferK[cSrcPtr[3]]; - } else { - cSrc3 = div255(aDest * cDest3); - } - - //----- source alpha - aSrc = shape; - - //----- special case for aSrc = 255 - if (aSrc == 255) { - aResult = 255; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - cResult3 = cSrc3; - } else { - - //----- special case for aDest = 0 - if (aDest == 0) { - aResult = aSrc; - cResult0 = cSrc0; - cResult1 = cSrc1; - cResult2 = cSrc2; - cResult3 = cSrc3; - } else { - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI); - } - } - - //----- write destination pixel - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr[3] = cResult3; - destColorPtr += 4; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} -#endif - - -// special case: -// !pipe->pattern && !pipe->noTransparency && !state->softMask && -// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && -// !pipe->nonIsolatedGroup && -// bitmap->mode == splashModeMono1 && !bitmap->alpha -void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, cSrc0, cDest0, cResult0; - SplashColorPtr destColorPtr; - Guchar destColorMask; - SplashScreenCursor screenCursor; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - destColorMask = (Guchar)(0x80 >> (x0 & 7)); - - screenCursor = state->screen->getTestCursor(y); - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; - - //----- source color - cSrc0 = state->grayTransfer[cSrcPtr[0]]; - - //----- source alpha - aSrc = div255(pipe->aInput * shape); - - //----- result color - // note: aDest = alphaI = aResult = 0xff - cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0); - - //----- write destination pixel - if (state->screen->testWithCursor(screenCursor, x, cResult0)) { - *destColorPtr |= destColorMask; - } else { - *destColorPtr &= (Guchar)~destColorMask; - } - destColorPtr += destColorMask & 1; - destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && !pipe->noTransparency && !state->softMask && -// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && -// !pipe->nonIsolatedGroup && -// bitmap->mode == splashModeMono8 && bitmap->alpha -void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 1; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - ++destColorPtr; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = *destColorPtr; - aDest = *destAlphaPtr; - - //----- source color - cSrc0 = state->grayTransfer[cSrcPtr[0]]; - - //----- source alpha - aSrc = div255(pipe->aInput * shape); - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - if (alphaI == 0) { - cResult0 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - } - - //----- write destination pixel - *destColorPtr++ = cResult0; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && !pipe->noTransparency && !state->softMask && -// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && -// !pipe->nonIsolatedGroup && -// bitmap->mode == splashModeRGB8 && bitmap->alpha -void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2; - Guchar cDest0, cDest1, cDest2; - Guchar cResult0, cResult1, cResult2; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 3; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = destColorPtr[0]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[2]; - aDest = *destAlphaPtr; - - //----- source color - cSrc0 = state->rgbTransferR[cSrcPtr[0]]; - cSrc1 = state->rgbTransferG[cSrcPtr[1]]; - cSrc2 = state->rgbTransferB[cSrcPtr[2]]; - - //----- source alpha - aSrc = div255(pipe->aInput * shape); - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - } - - //----- write destination pixel - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr += 3; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -// special case: -// !pipe->pattern && !pipe->noTransparency && !state->softMask && -// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && -// !pipe->nonIsolatedGroup && -// bitmap->mode == splashModeBGR8 && bitmap->alpha -void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2; - Guchar cDest0, cDest1, cDest2; - Guchar cResult0, cResult1, cResult2; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 3; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 3; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = destColorPtr[2]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[0]; - aDest = *destAlphaPtr; - - //----- source color - cSrc0 = state->rgbTransferR[cSrcPtr[0]]; - cSrc1 = state->rgbTransferG[cSrcPtr[1]]; - cSrc2 = state->rgbTransferB[cSrcPtr[2]]; - - //----- source alpha - aSrc = div255(pipe->aInput * shape); - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - } - - //----- write destination pixel - destColorPtr[0] = cResult2; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult0; - destColorPtr += 3; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} - -#if SPLASH_CMYK -// special case: -// !pipe->pattern && !pipe->noTransparency && !state->softMask && -// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && -// !pipe->nonIsolatedGroup && -// bitmap->mode == splashModeCMYK8 && bitmap->alpha -void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr) { - Guchar shape, aSrc, aDest, alphaI, aResult; - Guchar cSrc0, cSrc1, cSrc2, cSrc3; - Guchar cDest0, cDest1, cDest2, cDest3; - Guchar cResult0, cResult1, cResult2, cResult3; - SplashColorPtr destColorPtr; - Guchar *destAlphaPtr; - int cSrcStride, x, lastX; - - if (cSrcPtr) { - cSrcStride = 4; - } else { - cSrcPtr = pipe->cSrcVal; - cSrcStride = 0; - } - for (; x0 <= x1; ++x0) { - if (*shapePtr) { - break; - } - cSrcPtr += cSrcStride; - ++shapePtr; - } - if (x0 > x1) { - return; - } - updateModX(x0); - updateModY(y); - lastX = x0; - - destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; - - for (x = x0; x <= x1; ++x) { - - //----- shape - shape = *shapePtr; - if (!shape) { - destColorPtr += 4; - ++destAlphaPtr; - cSrcPtr += cSrcStride; - ++shapePtr; - continue; - } - lastX = x; - - //----- read destination pixel - cDest0 = destColorPtr[0]; - cDest1 = destColorPtr[1]; - cDest2 = destColorPtr[2]; - cDest3 = destColorPtr[3]; - aDest = *destAlphaPtr; - - //----- overprint - if (state->overprintMask & 1) { - cSrc0 = state->cmykTransferC[cSrcPtr[0]]; - } else { - cSrc0 = div255(aDest * cDest0); - } - if (state->overprintMask & 2) { - cSrc1 = state->cmykTransferM[cSrcPtr[1]]; - } else { - cSrc1 = div255(aDest * cDest1); - } - if (state->overprintMask & 4) { - cSrc2 = state->cmykTransferY[cSrcPtr[2]]; - } else { - cSrc2 = div255(aDest * cDest2); - } - if (state->overprintMask & 8) { - cSrc3 = state->cmykTransferK[cSrcPtr[3]]; - } else { - cSrc3 = div255(aDest * cDest3); - } - - //----- source alpha - aSrc = div255(pipe->aInput * shape); - - //----- result alpha and non-isolated group element correction - aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); - alphaI = aResult; - - //----- result color - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - cResult3 = 0; - } else { - cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); - cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); - cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); - cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI); - } - - //----- write destination pixel - destColorPtr[0] = cResult0; - destColorPtr[1] = cResult1; - destColorPtr[2] = cResult2; - destColorPtr[3] = cResult3; - destColorPtr += 4; - *destAlphaPtr++ = aResult; - - cSrcPtr += cSrcStride; - ++shapePtr; - } - - updateModX(lastX); -} -#endif - - -//------------------------------------------------------------------------ - -// Transform a point from user space to device space. -inline void Splash::transform(SplashCoord *matrix, - SplashCoord xi, SplashCoord yi, - SplashCoord *xo, SplashCoord *yo) { - // [ m[0] m[1] 0 ] - // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] - // [ m[4] m[5] 1 ] - *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; - *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; -} - -//------------------------------------------------------------------------ -// Splash -//------------------------------------------------------------------------ - -Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, - SplashScreenParams *screenParams) { - bitmap = bitmapA; - bitmapComps = splashColorModeNComps[bitmap->mode]; - vectorAntialias = vectorAntialiasA; - inShading = gFalse; - state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, - screenParams); - scanBuf = (Guchar *)gmalloc(bitmap->width); - if (bitmap->mode == splashModeMono1) { - scanBuf2 = (Guchar *)gmalloc(bitmap->width); - } else { - scanBuf2 = NULL; - } - groupBackBitmap = NULL; - minLineWidth = 0; - clearModRegion(); - debugMode = gFalse; -} - -Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, - SplashScreen *screenA) { - bitmap = bitmapA; - bitmapComps = splashColorModeNComps[bitmap->mode]; - vectorAntialias = vectorAntialiasA; - inShading = gFalse; - state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, - screenA); - scanBuf = (Guchar *)gmalloc(bitmap->width); - if (bitmap->mode == splashModeMono1) { - scanBuf2 = (Guchar *)gmalloc(bitmap->width); - } else { - scanBuf2 = NULL; - } - groupBackBitmap = NULL; - minLineWidth = 0; - clearModRegion(); - debugMode = gFalse; -} - -Splash::~Splash() { - while (state->next) { - restoreState(); - } - delete state; - gfree(scanBuf); - gfree(scanBuf2); -} - -//------------------------------------------------------------------------ -// state read -//------------------------------------------------------------------------ - -SplashCoord *Splash::getMatrix() { - return state->matrix; -} - -SplashPattern *Splash::getStrokePattern() { - return state->strokePattern; -} - -SplashPattern *Splash::getFillPattern() { - return state->fillPattern; -} - -SplashScreen *Splash::getScreen() { - return state->screen; -} - -SplashBlendFunc Splash::getBlendFunc() { - return state->blendFunc; -} - -SplashCoord Splash::getStrokeAlpha() { - return state->strokeAlpha; -} - -SplashCoord Splash::getFillAlpha() { - return state->fillAlpha; -} - -SplashCoord Splash::getLineWidth() { - return state->lineWidth; -} - -int Splash::getLineCap() { - return state->lineCap; -} - -int Splash::getLineJoin() { - return state->lineJoin; -} - -SplashCoord Splash::getMiterLimit() { - return state->miterLimit; -} - -SplashCoord Splash::getFlatness() { - return state->flatness; -} - -SplashCoord *Splash::getLineDash() { - return state->lineDash; -} - -int Splash::getLineDashLength() { - return state->lineDashLength; -} - -SplashCoord Splash::getLineDashPhase() { - return state->lineDashPhase; -} - -SplashStrokeAdjustMode Splash::getStrokeAdjust() { - return state->strokeAdjust; -} - -SplashClip *Splash::getClip() { - return state->clip; -} - -SplashBitmap *Splash::getSoftMask() { - return state->softMask; -} - -GBool Splash::getInNonIsolatedGroup() { - return state->inNonIsolatedGroup; -} - -GBool Splash::getInKnockoutGroup() { - return state->inKnockoutGroup; -} - -//------------------------------------------------------------------------ -// state write -//------------------------------------------------------------------------ - -void Splash::setMatrix(SplashCoord *matrix) { - memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord)); -} - -void Splash::setStrokePattern(SplashPattern *strokePattern) { - state->setStrokePattern(strokePattern); -} - -void Splash::setFillPattern(SplashPattern *fillPattern) { - state->setFillPattern(fillPattern); -} - -void Splash::setScreen(SplashScreen *screen) { - state->setScreen(screen); -} - -void Splash::setBlendFunc(SplashBlendFunc func) { - state->blendFunc = func; -} - -void Splash::setStrokeAlpha(SplashCoord alpha) { - state->strokeAlpha = alpha; -} - -void Splash::setFillAlpha(SplashCoord alpha) { - state->fillAlpha = alpha; -} - -void Splash::setLineWidth(SplashCoord lineWidth) { - state->lineWidth = lineWidth; -} - -void Splash::setLineCap(int lineCap) { - if (lineCap >= 0 && lineCap <= 2) { - state->lineCap = lineCap; - } else { - state->lineCap = 0; - } -} - -void Splash::setLineJoin(int lineJoin) { - if (lineJoin >= 0 && lineJoin <= 2) { - state->lineJoin = lineJoin; - } else { - state->lineJoin = 0; - } -} - -void Splash::setMiterLimit(SplashCoord miterLimit) { - state->miterLimit = miterLimit; -} - -void Splash::setFlatness(SplashCoord flatness) { - if (flatness < 1) { - state->flatness = 1; - } else { - state->flatness = flatness; - } -} - -void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength, - SplashCoord lineDashPhase) { - state->setLineDash(lineDash, lineDashLength, lineDashPhase); -} - -void Splash::setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust) { - state->strokeAdjust = strokeAdjust; -} - -void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1) { - state->clipResetToRect(x0, y0, x1, y1); -} - -SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1) { - return state->clipToRect(x0, y0, x1, y1); -} - -SplashError Splash::clipToPath(SplashPath *path, GBool eo) { - return state->clipToPath(path, eo); -} - -void Splash::setSoftMask(SplashBitmap *softMask) { - state->setSoftMask(softMask); -} - -void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA, - int groupBackXA, int groupBackYA, - GBool nonIsolated, GBool knockout) { - groupBackBitmap = groupBackBitmapA; - groupBackX = groupBackXA; - groupBackY = groupBackYA; - state->inNonIsolatedGroup = nonIsolated; - state->inKnockoutGroup = knockout; -} - -void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue, - Guchar *gray) { - state->setTransfer(red, green, blue, gray); -} - -void Splash::setOverprintMask(Guint overprintMask) { - state->overprintMask = overprintMask; -} - - -void Splash::setEnablePathSimplification(GBool en) { - state->enablePathSimplification = en; -} - -//------------------------------------------------------------------------ -// state save/restore -//------------------------------------------------------------------------ - -void Splash::saveState() { - SplashState *newState; - - newState = state->copy(); - newState->next = state; - state = newState; -} - -SplashError Splash::restoreState() { - SplashState *oldState; - - if (!state->next) { - return splashErrNoSave; - } - oldState = state; - state = state->next; - delete oldState; - return splashOk; -} - -//------------------------------------------------------------------------ -// drawing operations -//------------------------------------------------------------------------ - -void Splash::clear(SplashColorPtr color, Guchar alpha) { - SplashColorPtr row, p; - Guchar mono; - int x, y; - - switch (bitmap->mode) { - case splashModeMono1: - mono = (color[0] & 0x80) ? 0xff : 0x00; - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - mono, -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, mono, bitmap->rowSize * bitmap->height); - } - break; - case splashModeMono8: - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - color[0], -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); - } - break; - case splashModeRGB8: - if (color[0] == color[1] && color[1] == color[2]) { - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - color[0], -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); - } - } else { - row = bitmap->data; - for (y = 0; y < bitmap->height; ++y) { - p = row; - for (x = 0; x < bitmap->width; ++x) { - *p++ = color[0]; - *p++ = color[1]; - *p++ = color[2]; - } - row += bitmap->rowSize; - } - } - break; - case splashModeBGR8: - if (color[0] == color[1] && color[1] == color[2]) { - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - color[0], -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); - } - } else { - row = bitmap->data; - for (y = 0; y < bitmap->height; ++y) { - p = row; - for (x = 0; x < bitmap->width; ++x) { - *p++ = color[2]; - *p++ = color[1]; - *p++ = color[0]; - } - row += bitmap->rowSize; - } - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) { - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - color[0], -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); - } - } else { - row = bitmap->data; - for (y = 0; y < bitmap->height; ++y) { - p = row; - for (x = 0; x < bitmap->width; ++x) { - *p++ = color[0]; - *p++ = color[1]; - *p++ = color[2]; - *p++ = color[3]; - } - row += bitmap->rowSize; - } - } - break; -#endif - } - - if (bitmap->alpha) { - memset(bitmap->alpha, alpha, bitmap->alphaRowSize * bitmap->height); - } - - updateModX(0); - updateModY(0); - updateModX(bitmap->width - 1); - updateModY(bitmap->height - 1); -} - -SplashError Splash::stroke(SplashPath *path) { - SplashPath *path2, *dPath; - SplashCoord t0, t1, t2, t3, w, w2, lineDashMax, lineDashTotal; - int lineCap, lineJoin, i; - - if (debugMode) { - printf("stroke [dash:%d] [width:%.2f]:\n", - state->lineDashLength, (double)state->lineWidth); - dumpPath(path); - } - opClipRes = splashClipAllOutside; - if (path->length == 0) { - return splashErrEmptyPath; - } - path2 = flattenPath(path, state->matrix, state->flatness); - - // Compute an approximation of the transformed line width. - // Given a CTM of [m0 m1], - // [m2 m3] - // if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else - // use min{|m1|,|m2|}. - // This handles the common cases -- [s 0 ] and [0 s] -- - // [0 +/-s] [+/-s 0] - // well, and still does something reasonable for the uncommon - // case transforms. - t0 = splashAbs(state->matrix[0]); - t1 = splashAbs(state->matrix[1]); - t2 = splashAbs(state->matrix[2]); - t3 = splashAbs(state->matrix[3]); - if (t0 * t3 >= t1 * t2) { - w = (t0 < t3) ? t0 : t3; - } else { - w = (t1 < t2) ? t1 : t2; - } - w2 = w * state->lineWidth; - - // construct the dashed path - if (state->lineDashLength > 0) { - - // check the maximum transformed dash element length (using the - // same approximation as for line width) -- if it's less than 0.1 - // pixel, don't apply the dash pattern; this avoids a huge - // performance/memory hit with PDF files that use absurd dash - // patterns like [0.0007 0.0003] - lineDashTotal = 0; - lineDashMax = 0; - for (i = 0; i < state->lineDashLength; ++i) { - lineDashTotal += state->lineDash[i]; - if (state->lineDash[i] > lineDashMax) { - lineDashMax = state->lineDash[i]; - } - } - // Acrobat simply draws nothing if the dash array is [0] - if (lineDashTotal == 0) { - delete path2; - return splashOk; - } - if (w * lineDashMax > 0.1) { - - dPath = makeDashedPath(path2); - delete path2; - path2 = dPath; - if (path2->length == 0) { - delete path2; - return splashErrEmptyPath; - } - } - } - - // round caps on narrow lines look bad, and can't be - // stroke-adjusted, so use projecting caps instead (but we can't do - // this if there are zero-length dashes or segments, because those - // turn into round dots) - lineCap = state->lineCap; - lineJoin = state->lineJoin; - if (state->strokeAdjust == splashStrokeAdjustCAD && - w2 < 3.5) { - if (lineCap == splashLineCapRound && - !state->lineDashContainsZeroLengthDashes() && - !path->containsZeroLengthSubpaths()) { - lineCap = splashLineCapProjecting; - } - if (lineJoin == splashLineJoinRound) { - lineJoin = splashLineJoinBevel; - } - } - - // if there is a min line width set, and the transformed line width - // is smaller, use the min line width - if (w > 0 && w2 < minLineWidth) { - strokeWide(path2, minLineWidth / w, splashLineCapButt, splashLineJoinBevel); - } else if (bitmap->mode == splashModeMono1 || !vectorAntialias) { - // in monochrome mode or if antialiasing is disabled, use 0-width - // lines for any transformed line width <= 1 -- lines less than 1 - // pixel wide look too fat without antialiasing - if (w2 < 1.001) { - strokeNarrow(path2); - } else { - strokeWide(path2, state->lineWidth, lineCap, lineJoin); - } - } else { - // in gray and color modes, only use 0-width lines if the line - // width is explicitly set to 0 - if (state->lineWidth == 0) { - strokeNarrow(path2); - } else { - strokeWide(path2, state->lineWidth, lineCap, lineJoin); - } - } - - delete path2; - return splashOk; -} - -void Splash::strokeNarrow(SplashPath *path) { - SplashPipe pipe; - SplashXPath *xPath; - SplashXPathSeg *seg; - int x0, x1, y0, y1, xa, xb, y; - SplashCoord dxdy; - SplashClipResult clipRes; - int nClipRes[3]; - int i; - - nClipRes[0] = nClipRes[1] = nClipRes[2] = 0; - - xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse, - state->enablePathSimplification, - state->strokeAdjust); - - pipeInit(&pipe, state->strokePattern, - (Guchar)splashRound(state->strokeAlpha * 255), - gTrue, gFalse); - - for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { - if (seg->y0 <= seg->y1) { - y0 = splashFloor(seg->y0); - y1 = splashFloor(seg->y1); - x0 = splashFloor(seg->x0); - x1 = splashFloor(seg->x1); - } else { - y0 = splashFloor(seg->y1); - y1 = splashFloor(seg->y0); - x0 = splashFloor(seg->x1); - x1 = splashFloor(seg->x0); - } - if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, - x0 <= x1 ? x1 : x0, y1, - state->strokeAdjust)) - != splashClipAllOutside) { - if (y0 == y1) { - if (x0 <= x1) { - drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); - } else { - drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside); - } - } else { - dxdy = seg->dxdy; - y = state->clip->getYMinI(state->strokeAdjust); - if (y0 < y) { - y0 = y; - x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy); - } - y = state->clip->getYMaxI(state->strokeAdjust); - if (y1 > y) { - y1 = y; - x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy); - } - if (x0 <= x1) { - xa = x0; - for (y = y0; y <= y1; ++y) { - if (y < y1) { - xb = splashFloor(seg->x0 + - ((SplashCoord)y + 1 - seg->y0) * dxdy); - } else { - xb = x1 + 1; - } - if (xa == xb) { - drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); - } else { - drawStrokeSpan(&pipe, xa, xb - 1, y, - clipRes == splashClipAllInside); - } - xa = xb; - } - } else { - xa = x0; - for (y = y0; y <= y1; ++y) { - if (y < y1) { - xb = splashFloor(seg->x0 + - ((SplashCoord)y + 1 - seg->y0) * dxdy); - } else { - xb = x1 - 1; - } - if (xa == xb) { - drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); - } else { - drawStrokeSpan(&pipe, xb + 1, xa, y, - clipRes == splashClipAllInside); - } - xa = xb; - } - } - } - } - ++nClipRes[clipRes]; - } - if (nClipRes[splashClipPartial] || - (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) { - opClipRes = splashClipPartial; - } else if (nClipRes[splashClipAllInside]) { - opClipRes = splashClipAllInside; - } else { - opClipRes = splashClipAllOutside; - } - - delete xPath; -} - -void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, - GBool noClip) { - int x; - - x = state->clip->getXMinI(state->strokeAdjust); - if (x > x0) { - x0 = x; - } - x = state->clip->getXMaxI(state->strokeAdjust); - if (x < x1) { - x1 = x; - } - if (x0 > x1) { - return; - } - for (x = x0; x <= x1; ++x) { - scanBuf[x] = 0xff; - } - if (!noClip) { - if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) { - return; - } - } - (this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, NULL); -} - -void Splash::strokeWide(SplashPath *path, SplashCoord w, - int lineCap, int lineJoin) { - SplashPath *path2; - - path2 = makeStrokePath(path, w, lineCap, lineJoin, gFalse); - fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha); - delete path2; -} - -SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness) { - SplashPath *fPath; - SplashCoord flatness2; - Guchar flag; - int i; - - fPath = new SplashPath(); -#if USE_FIXEDPOINT - flatness2 = flatness; -#else - flatness2 = flatness * flatness; -#endif - i = 0; - while (i < path->length) { - flag = path->flags[i]; - if (flag & splashPathFirst) { - fPath->moveTo(path->pts[i].x, path->pts[i].y); - ++i; - } else { - if (flag & splashPathCurve) { - flattenCurve(path->pts[i-1].x, path->pts[i-1].y, - path->pts[i ].x, path->pts[i ].y, - path->pts[i+1].x, path->pts[i+1].y, - path->pts[i+2].x, path->pts[i+2].y, - matrix, flatness2, fPath); - i += 3; - } else { - fPath->lineTo(path->pts[i].x, path->pts[i].y); - ++i; - } - if (path->flags[i-1] & splashPathClosed) { - fPath->close(); - } - } - } - return fPath; -} - -void Splash::flattenCurve(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1, - SplashCoord x2, SplashCoord y2, - SplashCoord x3, SplashCoord y3, - SplashCoord *matrix, SplashCoord flatness2, - SplashPath *fPath) { - SplashCoord cx[splashMaxCurveSplits + 1][3]; - SplashCoord cy[splashMaxCurveSplits + 1][3]; - int cNext[splashMaxCurveSplits + 1]; - SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; - SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; - SplashCoord dx, dy, mx, my, tx, ty, d1, d2; - int p1, p2, p3; - - // initial segment - p1 = 0; - p2 = splashMaxCurveSplits; - cx[p1][0] = x0; cy[p1][0] = y0; - cx[p1][1] = x1; cy[p1][1] = y1; - cx[p1][2] = x2; cy[p1][2] = y2; - cx[p2][0] = x3; cy[p2][0] = y3; - cNext[p1] = p2; - - while (p1 < splashMaxCurveSplits) { - - // get the next segment - xl0 = cx[p1][0]; yl0 = cy[p1][0]; - xx1 = cx[p1][1]; yy1 = cy[p1][1]; - xx2 = cx[p1][2]; yy2 = cy[p1][2]; - p2 = cNext[p1]; - xr3 = cx[p2][0]; yr3 = cy[p2][0]; - - // compute the distances (in device space) from the control points - // to the midpoint of the straight line (this is a bit of a hack, - // but it's much faster than computing the actual distances to the - // line) - transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my); - transform(matrix, xx1, yy1, &tx, &ty); -#if USE_FIXEDPOINT - d1 = splashDist(tx, ty, mx, my); -#else - dx = tx - mx; - dy = ty - my; - d1 = dx*dx + dy*dy; -#endif - transform(matrix, xx2, yy2, &tx, &ty); -#if USE_FIXEDPOINT - d2 = splashDist(tx, ty, mx, my); -#else - dx = tx - mx; - dy = ty - my; - d2 = dx*dx + dy*dy; -#endif - - // if the curve is flat enough, or no more subdivisions are - // allowed, add the straight line segment - if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { - fPath->lineTo(xr3, yr3); - p1 = p2; - - // otherwise, subdivide the curve - } else { - xl1 = splashAvg(xl0, xx1); - yl1 = splashAvg(yl0, yy1); - xh = splashAvg(xx1, xx2); - yh = splashAvg(yy1, yy2); - xl2 = splashAvg(xl1, xh); - yl2 = splashAvg(yl1, yh); - xr2 = splashAvg(xx2, xr3); - yr2 = splashAvg(yy2, yr3); - xr1 = splashAvg(xh, xr2); - yr1 = splashAvg(yh, yr2); - xr0 = splashAvg(xl2, xr1); - yr0 = splashAvg(yl2, yr1); - // add the new subdivision points - p3 = (p1 + p2) / 2; - cx[p1][1] = xl1; cy[p1][1] = yl1; - cx[p1][2] = xl2; cy[p1][2] = yl2; - cNext[p1] = p3; - cx[p3][0] = xr0; cy[p3][0] = yr0; - cx[p3][1] = xr1; cy[p3][1] = yr1; - cx[p3][2] = xr2; cy[p3][2] = yr2; - cNext[p3] = p2; - } - } -} - -SplashPath *Splash::makeDashedPath(SplashPath *path) { - SplashPath *dPath; - SplashCoord lineDashTotal; - SplashCoord lineDashStartPhase, lineDashDist, segLen; - SplashCoord x0, y0, x1, y1, xa, ya; - GBool lineDashStartOn, lineDashEndOn, lineDashOn, newPath; - int lineDashStartIdx, lineDashIdx, subpathStart, nDashes; - int i, j, k; - - lineDashTotal = 0; - for (i = 0; i < state->lineDashLength; ++i) { - lineDashTotal += state->lineDash[i]; - } - // Acrobat simply draws nothing if the dash array is [0] - if (lineDashTotal == 0) { - return new SplashPath(); - } - lineDashStartPhase = state->lineDashPhase; - if (lineDashStartPhase > lineDashTotal * 2) { - i = splashFloor(lineDashStartPhase / (lineDashTotal * 2)); - lineDashStartPhase -= lineDashTotal * i * 2; - } else if (lineDashStartPhase < 0) { - i = splashCeil(-lineDashStartPhase / (lineDashTotal * 2)); - lineDashStartPhase += lineDashTotal * i * 2; - } - i = splashFloor(lineDashStartPhase / lineDashTotal); - lineDashStartPhase -= (SplashCoord)i * lineDashTotal; - lineDashStartOn = gTrue; - lineDashStartIdx = 0; - if (lineDashStartPhase > 0) { - while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { - lineDashStartOn = !lineDashStartOn; - lineDashStartPhase -= state->lineDash[lineDashStartIdx]; - if (++lineDashStartIdx == state->lineDashLength) { - lineDashStartIdx = 0; - } - } - } - - dPath = new SplashPath(); - - // process each subpath - i = 0; - while (i < path->length) { - - // find the end of the subpath - for (j = i; - j < path->length - 1 && !(path->flags[j] & splashPathLast); - ++j) ; - - // initialize the dash parameters - lineDashOn = lineDashStartOn; - lineDashEndOn = lineDashStartOn; - lineDashIdx = lineDashStartIdx; - lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; - subpathStart = dPath->length; - nDashes = 0; - - // process each segment of the subpath - newPath = gTrue; - for (k = i; k < j; ++k) { - - // grab the segment - x0 = path->pts[k].x; - y0 = path->pts[k].y; - x1 = path->pts[k+1].x; - y1 = path->pts[k+1].y; - segLen = splashDist(x0, y0, x1, y1); - - // process the segment - while (segLen > 0) { - - // Special case for zero-length dash segments: draw a very - // short -- but not zero-length -- segment. This ensures that - // we get the correct behavior with butt and projecting line - // caps. The PS/PDF specs imply that zero-length segments are - // not drawn unless the line cap is round, but Acrobat and - // Ghostscript both draw very short segments (for butt caps) - // and squares (for projecting caps). - if (lineDashDist == 0) { - if (lineDashOn) { - if (newPath) { - dPath->moveTo(x0, y0); - newPath = gFalse; - ++nDashes; - } - xa = x0 + ((SplashCoord)0.001 / segLen) * (x1 - x0); - ya = y0 + ((SplashCoord)0.001 / segLen) * (y1 - y0); - dPath->lineTo(xa, ya); - } - - } else if (lineDashDist >= segLen) { - if (lineDashOn) { - if (newPath) { - dPath->moveTo(x0, y0); - newPath = gFalse; - ++nDashes; - } - dPath->lineTo(x1, y1); - } - lineDashDist -= segLen; - segLen = 0; - - } else { - xa = x0 + (lineDashDist / segLen) * (x1 - x0); - ya = y0 + (lineDashDist / segLen) * (y1 - y0); - if (lineDashOn) { - if (newPath) { - dPath->moveTo(x0, y0); - newPath = gFalse; - ++nDashes; - } - dPath->lineTo(xa, ya); - } - x0 = xa; - y0 = ya; - segLen -= lineDashDist; - lineDashDist = 0; - } - - lineDashEndOn = lineDashOn; - - // get the next entry in the dash array - if (lineDashDist <= 0) { - lineDashOn = !lineDashOn; - if (++lineDashIdx == state->lineDashLength) { - lineDashIdx = 0; - } - lineDashDist = state->lineDash[lineDashIdx]; - newPath = gTrue; - } - } - } - - // in a closed subpath, where the dash pattern is "on" at both the - // start and end of the subpath, we need to merge the start and - // end to get a proper line join - if ((path->flags[j] & splashPathClosed) && - lineDashStartOn && - lineDashEndOn) { - if (nDashes == 1) { - dPath->close(); - } else if (nDashes > 1) { - k = subpathStart; - do { - ++k; - dPath->lineTo(dPath->pts[k].x, dPath->pts[k].y); - } while (!(dPath->flags[k] & splashPathLast)); - ++k; - memmove(&dPath->pts[subpathStart], &dPath->pts[k], - (dPath->length - k) * sizeof(SplashPathPoint)); - memmove(&dPath->flags[subpathStart], &dPath->flags[k], - (dPath->length - k) * sizeof(Guchar)); - dPath->length -= k - subpathStart; - dPath->curSubpath -= k - subpathStart; - } - } - - i = j + 1; - } - - return dPath; -} - -SplashError Splash::fill(SplashPath *path, GBool eo) { - if (debugMode) { - printf("fill [eo:%d]:\n", eo); - dumpPath(path); - } - return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha); -} - -SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, - SplashPattern *pattern, - SplashCoord alpha) { - SplashPipe pipe; - SplashPath *path2; - SplashXPath *xPath; - SplashXPathScanner *scanner; - int xMin, yMin, xMax, xMin2, xMax2, yMax, y, t; - SplashClipResult clipRes; - - if (path->length == 0) { - return splashErrEmptyPath; - } - if (pathAllOutside(path)) { - opClipRes = splashClipAllOutside; - return splashOk; - } - - path2 = tweakFillPath(path); - - xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue, - state->enablePathSimplification, - state->strokeAdjust); - if (path2 != path) { - delete path2; - } - xMin = xPath->getXMin(); - yMin = xPath->getYMin(); - xMax = xPath->getXMax(); - yMax = xPath->getYMax(); - if (xMin > xMax || yMin > yMax) { - delete xPath; - return splashOk; - } - scanner = new SplashXPathScanner(xPath, eo, yMin, yMax); - - // check clipping - if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, - state->strokeAdjust)) - != splashClipAllOutside) { - - if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { - xMin = t; - } - if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { - xMax = t; - } - if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { - yMin = t; - } - if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { - yMax = t; - } - if (xMin > xMax || yMin > yMax) { - delete scanner; - delete xPath; - return splashOk; - } - - pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255), - gTrue, gFalse); - - // draw the spans - if (vectorAntialias && !inShading) { - for (y = yMin; y <= yMax; ++y) { - scanner->getSpan(scanBuf, y, xMin, xMax, &xMin2, &xMax2); - if (xMin2 <= xMax2) { - if (clipRes != splashClipAllInside) { - state->clip->clipSpan(scanBuf, y, xMin2, xMax2, - state->strokeAdjust); - } - (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL); - } - } - } else { - for (y = yMin; y <= yMax; ++y) { - scanner->getSpanBinary(scanBuf, y, xMin, xMax, &xMin2, &xMax2); - if (xMin2 <= xMax2) { - if (clipRes != splashClipAllInside) { - state->clip->clipSpanBinary(scanBuf, y, xMin2, xMax2, - state->strokeAdjust); - } - (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL); - } - } - } - } - opClipRes = clipRes; - - delete scanner; - delete xPath; - return splashOk; -} - -// Applies various tweaks to a fill path: -// (1) add stroke adjust hints to a filled rectangle -// (2) applies a minimum width to a zero-width filled rectangle (so -// stroke adjustment works correctly -// (3) convert a degenerate fill ('moveto lineto fill' and 'moveto -// lineto closepath fill') to a minimum-width filled rectangle -// -// These tweaks only apply to paths with a single subpath. -// -// Returns either the unchanged input path or a new path (in which -// case the returned path must be deleted by the caller). -SplashPath *Splash::tweakFillPath(SplashPath *path) { - SplashPath *path2; - SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w; - int n; - - if (state->strokeAdjust == splashStrokeAdjustOff || path->hints) { - return path; - } - - n = path->getLength(); - if (!((n == 2) || - (n == 3 && - path->flags[1] == 0) || - (n == 4 && - path->flags[1] == 0 && - path->flags[2] == 0) || - (n == 5 && - path->flags[1] == 0 && - path->flags[2] == 0 && - path->flags[3] == 0))) { - return path; - } - - path2 = path; - - // degenerate fill (2 or 3 points) or rectangle of (nearly) zero - // width --> replace with a min-width rectangle and hint - if (n == 2 || - (n == 3 && (path->flags[0] & splashPathClosed)) || - (n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 && - splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) || - ((n == 4 || - (n == 5 && (path->flags[0] & splashPathClosed))) && - ((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 && - splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 && - splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 && - splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) || - (splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 && - splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 && - splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 && - splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) { - wx = state->matrix[0] + state->matrix[2]; - wy = state->matrix[1] + state->matrix[3]; - w = splashSqrt(wx*wx + wy*wy); - if (w < 0.001) { - w = 0; - } else { - // min width is 0.1 -- this constant is minWidth * sqrt(2) - w = (SplashCoord)0.1414 / w; - } - xx0 = path->pts[0].x; - yy0 = path->pts[0].y; - if (n <= 3) { - xx1 = path->pts[1].x; - yy1 = path->pts[1].y; - } else { - xx1 = path->pts[2].x; - yy1 = path->pts[2].y; - } - dx = xx1 - xx0; - dy = yy1 - yy0; - d = splashSqrt(dx * dx + dy * dy); - if (d < 0.001) { - d = 0; - } else { - d = w / d; - } - dx *= d; - dy *= d; - path2 = new SplashPath(); - path2->moveTo(xx0 + dy, yy0 - dx); - path2->lineTo(xx1 + dy, yy1 - dx); - path2->lineTo(xx1 - dy, yy1 + dx); - path2->lineTo(xx0 - dy, yy0 + dx); - path2->close(gTrue); - path2->addStrokeAdjustHint(0, 2, 0, 4); - path2->addStrokeAdjustHint(1, 3, 0, 4); - - // unclosed rectangle --> close and hint - } else if (n == 4 && !(path->flags[0] & splashPathClosed)) { - path2->close(gTrue); - path2->addStrokeAdjustHint(0, 2, 0, 4); - path2->addStrokeAdjustHint(1, 3, 0, 4); - - // closed rectangle --> hint - } else if (n == 5 && (path->flags[0] & splashPathClosed)) { - path2->addStrokeAdjustHint(0, 2, 0, 4); - path2->addStrokeAdjustHint(1, 3, 0, 4); - } - - return path2; -} - -GBool Splash::pathAllOutside(SplashPath *path) { - SplashCoord xMin1, yMin1, xMax1, yMax1; - SplashCoord xMin2, yMin2, xMax2, yMax2; - SplashCoord x, y; - int xMinI, yMinI, xMaxI, yMaxI; - int i; - - xMin1 = xMax1 = path->pts[0].x; - yMin1 = yMax1 = path->pts[0].y; - for (i = 1; i < path->length; ++i) { - if (path->pts[i].x < xMin1) { - xMin1 = path->pts[i].x; - } else if (path->pts[i].x > xMax1) { - xMax1 = path->pts[i].x; - } - if (path->pts[i].y < yMin1) { - yMin1 = path->pts[i].y; - } else if (path->pts[i].y > yMax1) { - yMax1 = path->pts[i].y; - } - } - - transform(state->matrix, xMin1, yMin1, &x, &y); - xMin2 = xMax2 = x; - yMin2 = yMax2 = y; - transform(state->matrix, xMin1, yMax1, &x, &y); - if (x < xMin2) { - xMin2 = x; - } else if (x > xMax2) { - xMax2 = x; - } - if (y < yMin2) { - yMin2 = y; - } else if (y > yMax2) { - yMax2 = y; - } - transform(state->matrix, xMax1, yMin1, &x, &y); - if (x < xMin2) { - xMin2 = x; - } else if (x > xMax2) { - xMax2 = x; - } - if (y < yMin2) { - yMin2 = y; - } else if (y > yMax2) { - yMax2 = y; - } - transform(state->matrix, xMax1, yMax1, &x, &y); - if (x < xMin2) { - xMin2 = x; - } else if (x > xMax2) { - xMax2 = x; - } - if (y < yMin2) { - yMin2 = y; - } else if (y > yMax2) { - yMax2 = y; - } - // sanity-check the coordinates - xMinI/yMinI/xMaxI/yMaxI are - // 32-bit integers, so coords need to be < 2^31 - SplashXPath::clampCoords(&xMin2, &yMin2); - SplashXPath::clampCoords(&xMax2, &yMax2); - xMinI = splashFloor(xMin2); - yMinI = splashFloor(yMin2); - xMaxI = splashFloor(xMax2); - yMaxI = splashFloor(yMax2); - - return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI, - state->strokeAdjust) == - splashClipAllOutside; -} - -SplashError Splash::fillChar(SplashCoord x, SplashCoord y, - int c, SplashFont *font) { - SplashGlyphBitmap glyph; - SplashCoord xt, yt; - int x0, y0, xFrac, yFrac; - SplashError err; - - if (debugMode) { - printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n", - (double)x, (double)y, c, c, c); - } - transform(state->matrix, x, y, &xt, &yt); - x0 = splashFloor(xt); - xFrac = splashFloor((xt - x0) * splashFontFraction); - y0 = splashFloor(yt); - yFrac = splashFloor((yt - y0) * splashFontFraction); - if (!font->getGlyph(c, xFrac, yFrac, &glyph)) { - return splashErrNoGlyph; - } - err = fillGlyph2(x0, y0, &glyph); - if (glyph.freeData) { - gfree(glyph.data); - } - return err; -} - -SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y, - SplashGlyphBitmap *glyph) { - SplashCoord xt, yt; - int x0, y0; - - transform(state->matrix, x, y, &xt, &yt); - x0 = splashFloor(xt); - y0 = splashFloor(yt); - return fillGlyph2(x0, y0, glyph); -} - -SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) { - SplashPipe pipe; - SplashClipResult clipRes; - Guchar alpha; - Guchar *p; - int xMin, yMin, xMax, yMax; - int x, y, xg, yg, xx, t; - - xg = x0 - glyph->x; - yg = y0 - glyph->y; - xMin = xg; - xMax = xg + glyph->w - 1; - yMin = yg; - yMax = yg + glyph->h - 1; - if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, - state->strokeAdjust)) - != splashClipAllOutside) { - pipeInit(&pipe, state->fillPattern, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - if (clipRes == splashClipAllInside) { - if (glyph->aa) { - p = glyph->data; - for (y = yMin; y <= yMax; ++y) { - (this->*pipe.run)(&pipe, xMin, xMax, y, - glyph->data + (y - yMin) * glyph->w, NULL); - } - } else { - p = glyph->data; - for (y = yMin; y <= yMax; ++y) { - for (x = xMin; x <= xMax; x += 8) { - alpha = *p++; - for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { - scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00; - alpha = (Guchar)(alpha << 1); - } - } - (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); - } - } - } else { - if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { - xMin = t; - } - if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { - xMax = t; - } - if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { - yMin = t; - } - if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { - yMax = t; - } - if (xMin <= xMax && yMin <= yMax) { - if (glyph->aa) { - for (y = yMin; y <= yMax; ++y) { - p = glyph->data + (y - yg) * glyph->w + (xMin - xg); - memcpy(scanBuf + xMin, p, xMax - xMin + 1); - state->clip->clipSpan(scanBuf, y, xMin, xMax, - state->strokeAdjust); - (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); - } - } else { - for (y = yMin; y <= yMax; ++y) { - p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3) - + ((xMin - xg) >> 3); - alpha = *p++; - xx = (xMin - xg) & 7; - alpha = (Guchar)(alpha << xx); - for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) { - scanBuf[x] = (alpha & 0x80) ? 255 : 0; - alpha = (Guchar)(alpha << 1); - } - for (; x <= xMax; x += 8) { - alpha = *p++; - for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { - scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0; - alpha = (Guchar)(alpha << 1); - } - } - state->clip->clipSpanBinary(scanBuf, y, xMin, xMax, - state->strokeAdjust); - (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); - } - } - } - } - } - opClipRes = clipRes; - - return splashOk; -} - -void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax, - int *xyMinI, int *xyMaxI) { - if (state->strokeAdjust == splashStrokeAdjustOff) { - *xyMinI = splashFloor(xyMin); - *xyMaxI = splashFloor(xyMax); - if (*xyMaxI <= *xyMinI) { - *xyMaxI = *xyMinI + 1; - } - } else { - splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI, state->strokeAdjust); - } -} - -// The glyphMode flag is not currently used, but may be useful if the -// stroke adjustment behavior is changed. -SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, - int w, int h, SplashCoord *mat, - GBool glyphMode, GBool interpolate) { - SplashBitmap *scaledMask; - SplashClipResult clipRes; - GBool minorAxisZero; - SplashCoord wSize, hSize, t0, t1; - int x0, y0, x1, y1, scaledWidth, scaledHeight; - - if (debugMode) { - printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", - w, h, (double)mat[0], (double)mat[1], (double)mat[2], - (double)mat[3], (double)mat[4], (double)mat[5]); - } - - // check for singular matrix - if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { - return splashErrSingularMatrix; - } - - minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; - - // rough estimate of size of scaled mask - t0 = splashAbs(mat[0]); - t1 = splashAbs(mat[1]); - wSize = t0 > t1 ? t0 : t1; - t0 = splashAbs(mat[2]); - t1 = splashAbs(mat[3]); - hSize = t0 > t1 ? t0 : t1; - - // stream-mode upscaling -- this is slower, so we only use it if the - // upscaled mask is large (in which case clipping should remove many - // pixels) -#if USE_FIXEDPOINT - if ((wSize > 2 * w && hSize > 2 * h && (int)wSize > 1000000 / (int)hSize) || - (wSize > w && hSize > h && (int)wSize > 10000000 / (int)hSize) || - ((wSize > w || hSize > h) && (int)wSize > 25000000 / (int)hSize)) { -#else - if ((wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) || - (wSize > w && hSize > h && wSize * hSize > 10000000) || - ((wSize > w || hSize > h) && wSize * hSize > 25000000)) { - upscaleMask(src, srcData, w, h, mat, glyphMode, interpolate); -#endif - - // scaling only - } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { - getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); - getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, - interpolate); - blitMask(scaledMask, x0, y0, clipRes); - delete scaledMask; - } - - // scaling plus vertical flip - } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { - getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); - getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, - interpolate); - vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); - blitMask(scaledMask, x0, y0, clipRes); - delete scaledMask; - } - - // scaling plus horizontal flip - } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) { - getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); - getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, - interpolate); - horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); - blitMask(scaledMask, x0, y0, clipRes); - delete scaledMask; - } - - // scaling plus horizontal and vertical flips - } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) { - getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); - getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, - interpolate); - vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); - horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); - blitMask(scaledMask, x0, y0, clipRes); - delete scaledMask; - } - - // all other cases - } else { - arbitraryTransformMask(src, srcData, w, h, mat, glyphMode, interpolate); - } - - return splashOk; -} - -// The glyphMode flag is not currently used, but may be useful if the -// stroke adjustment behavior is changed. -void Splash::upscaleMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool glyphMode, - GBool interpolate) { - SplashClipResult clipRes; - SplashPipe pipe; - Guchar *unscaledImage, *p; - SplashCoord xMin, yMin, xMax, yMax, t; - SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; - SplashCoord ix, iy, sx, sy, pix0, pix1; - int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt; - - // compute the bbox of the target quadrilateral - xMin = xMax = mat[4]; - t = mat[2] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - t = mat[0] + mat[2] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - t = mat[0] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - getImageBounds(xMin, xMax, &xMinI, &xMaxI); - yMin = yMax = mat[5]; - t = mat[3] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - t = mat[1] + mat[3] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - t = mat[1] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - getImageBounds(yMin, yMax, &yMinI, &yMaxI); - - // clipping - clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes == splashClipAllOutside) { - return; - } - if (clipRes != splashClipAllInside) { - if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { - xMinI = tt; - } - if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { - xMaxI = tt; - } - if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { - yMinI = tt; - } - if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { - yMaxI = tt; - } - } - - // invert the matrix - det = mat[0] * mat[3] - mat[1] * mat[2]; - if (splashAbs(det) < 1e-6) { - // this should be caught by the singular matrix check in fillImageMask - return; - } - det = (SplashCoord)1 / det; - mi0 = det * mat[3] * srcWidth; - mi1 = -det * mat[1] * srcHeight; - mi2 = -det * mat[2] * srcWidth; - mi3 = det * mat[0] * srcHeight; - mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; - mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; - - // grab the image - unscaledImage = (Guchar *)gmallocn(srcWidth, srcHeight); - for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth) { - (*src)(srcData, p); - for (x = 0; x < srcWidth; ++x) { - p[x] = (Guchar)(p[x] * 255); - } - } - - // draw it - pipeInit(&pipe, state->fillPattern, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - for (y = yMinI; y < yMaxI; ++y) { - for (x = xMinI; x < xMaxI; ++x) { - ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; - iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; - if (interpolate) { - if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { - x0 = splashFloor(ix - 0.5); - x1 = x0 + 1; - sx = (ix - 0.5) - x0; - y0 = splashFloor(iy - 0.5); - y1 = y0 + 1; - sy = (iy - 0.5) - y0; - if (x0 < 0) { - x0 = 0; - } - if (x1 >= srcWidth) { - x1 = srcWidth - 1; - } - if (y0 < 0) { - y0 = 0; - } - if (y1 >= srcHeight) { - y1 = srcHeight - 1; - } - pix0 = ((SplashCoord)1 - sx) - * (SplashCoord)unscaledImage[y0 * srcWidth + x0] - + sx * (SplashCoord)unscaledImage[y0 * srcWidth + x1]; - pix1 = ((SplashCoord)1 - sx) - * (SplashCoord)unscaledImage[y1 * srcWidth + x0] - + sx * (SplashCoord)unscaledImage[y1 * srcWidth + x1]; - scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 - + sy * pix1); - } else { - scanBuf[x] = 0; - } - } else { - x0 = splashFloor(ix); - y0 = splashFloor(iy); - if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { - scanBuf[x] = unscaledImage[y0 * srcWidth + x0]; - } else { - scanBuf[x] = 0; - } - } - } - if (clipRes != splashClipAllInside) { - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, - state->strokeAdjust); - } - } - (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, NULL); - } - - gfree(unscaledImage); -} - -// The glyphMode flag is not currently used, but may be useful if the -// stroke adjustment behavior is changed. -void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool glyphMode, - GBool interpolate) { - SplashBitmap *scaledMask; - SplashClipResult clipRes; - SplashPipe pipe; - int scaledWidth, scaledHeight, t0, t1; - SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; - SplashCoord vx[4], vy[4]; - int xMin, yMin, xMax, yMax; - ImageSection section[3]; - int nSections; - int bw, y, xa, xb, x, i, xx, yy; - - // compute the four vertices of the target quadrilateral - vx[0] = mat[4]; vy[0] = mat[5]; - vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; - vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; - vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; - - // clipping - xMin = splashRound(vx[0]); - xMax = splashRound(vx[0]); - yMin = splashRound(vy[0]); - yMax = splashRound(vy[0]); - for (i = 1; i < 4; ++i) { - t0 = splashRound(vx[i]); - if (t0 < xMin) { - xMin = t0; - } else if (t0 > xMax) { - xMax = t0; - } - t1 = splashRound(vy[i]); - if (t1 < yMin) { - yMin = t1; - } else if (t1 > yMax) { - yMax = t1; - } - } - clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes == splashClipAllOutside) { - return; - } - - // compute the scale factors - if (mat[0] >= 0) { - t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); - } else { - t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); - } - if (mat[1] >= 0) { - t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); - } else { - t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); - } - scaledWidth = t0 > t1 ? t0 : t1; - if (mat[2] >= 0) { - t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); - } else { - t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); - } - if (mat[3] >= 0) { - t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); - } else { - t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); - } - scaledHeight = t0 > t1 ? t0 : t1; - if (scaledWidth == 0) { - scaledWidth = 1; - } - if (scaledHeight == 0) { - scaledHeight = 1; - } - - // compute the inverse transform (after scaling) matrix - r00 = mat[0] / scaledWidth; - r01 = mat[1] / scaledWidth; - r10 = mat[2] / scaledHeight; - r11 = mat[3] / scaledHeight; - det = r00 * r11 - r01 * r10; - if (splashAbs(det) < 1e-6) { - // this should be caught by the singular matrix check in fillImageMask - return; - } - ir00 = r11 / det; - ir01 = -r01 / det; - ir10 = -r10 / det; - ir11 = r00 / det; - - // scale the input image - scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, interpolate); - - // construct the three sections - i = 0; - if (vy[1] < vy[i]) { - i = 1; - } - if (vy[2] < vy[i]) { - i = 2; - } - if (vy[3] < vy[i]) { - i = 3; - } - // NB: if using fixed point, 0.000001 will be truncated to zero, - // so these two comparisons must be <=, not < - if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && - vy[(i-1) & 3] < vy[(i+1) & 3]) { - i = (i-1) & 3; - } - if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { - section[0].y0 = splashRound(vy[i]); - section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; - if (vx[i] < vx[(i+1) & 3]) { - section[0].ia0 = i; - section[0].ia1 = (i+3) & 3; - section[0].ib0 = (i+1) & 3; - section[0].ib1 = (i+2) & 3; - } else { - section[0].ia0 = (i+1) & 3; - section[0].ia1 = (i+2) & 3; - section[0].ib0 = i; - section[0].ib1 = (i+3) & 3; - } - nSections = 1; - } else { - section[0].y0 = splashRound(vy[i]); - section[2].y1 = splashRound(vy[(i+2) & 3]) - 1; - section[0].ia0 = section[0].ib0 = i; - section[2].ia1 = section[2].ib1 = (i+2) & 3; - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[0].ia1 = section[2].ia0 = (i+1) & 3; - section[0].ib1 = section[2].ib0 = (i+3) & 3; - } else { - section[0].ia1 = section[2].ia0 = (i+3) & 3; - section[0].ib1 = section[2].ib0 = (i+1) & 3; - } - if (vy[(i+1) & 3] < vy[(i+3) & 3]) { - section[1].y0 = splashRound(vy[(i+1) & 3]); - section[2].y0 = splashRound(vy[(i+3) & 3]); - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[1].ia0 = (i+1) & 3; - section[1].ia1 = (i+2) & 3; - section[1].ib0 = i; - section[1].ib1 = (i+3) & 3; - } else { - section[1].ia0 = i; - section[1].ia1 = (i+3) & 3; - section[1].ib0 = (i+1) & 3; - section[1].ib1 = (i+2) & 3; - } - } else { - section[1].y0 = splashRound(vy[(i+3) & 3]); - section[2].y0 = splashRound(vy[(i+1) & 3]); - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[1].ia0 = i; - section[1].ia1 = (i+1) & 3; - section[1].ib0 = (i+3) & 3; - section[1].ib1 = (i+2) & 3; - } else { - section[1].ia0 = (i+3) & 3; - section[1].ia1 = (i+2) & 3; - section[1].ib0 = i; - section[1].ib1 = (i+1) & 3; - } - } - section[0].y1 = section[1].y0 - 1; - section[1].y1 = section[2].y0 - 1; - nSections = 3; - } - for (i = 0; i < nSections; ++i) { - section[i].xa0 = vx[section[i].ia0]; - section[i].ya0 = vy[section[i].ia0]; - section[i].xa1 = vx[section[i].ia1]; - section[i].ya1 = vy[section[i].ia1]; - section[i].xb0 = vx[section[i].ib0]; - section[i].yb0 = vy[section[i].ib0]; - section[i].xb1 = vx[section[i].ib1]; - section[i].yb1 = vy[section[i].ib1]; - section[i].dxdya = (section[i].xa1 - section[i].xa0) / - (section[i].ya1 - section[i].ya0); - section[i].dxdyb = (section[i].xb1 - section[i].xb0) / - (section[i].yb1 - section[i].yb0); - } - - // initialize the pixel pipe - pipeInit(&pipe, state->fillPattern, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - - // make sure narrow images cover at least one pixel - if (nSections == 1) { - if (section[0].y0 == section[0].y1) { - ++section[0].y1; - clipRes = opClipRes = splashClipPartial; - } - } else { - if (section[0].y0 == section[2].y1) { - ++section[1].y1; - clipRes = opClipRes = splashClipPartial; - } - } - - // scan all pixels inside the target region - bw = bitmap->width; - for (i = 0; i < nSections; ++i) { - for (y = section[i].y0; y <= section[i].y1; ++y) { - xa = splashRound(section[i].xa0 + - ((SplashCoord)y + 0.5 - section[i].ya0) * - section[i].dxdya); - xb = splashRound(section[i].xb0 + - ((SplashCoord)y + 0.5 - section[i].yb0) * - section[i].dxdyb); - if (xa > xb) { - continue; - } - // make sure narrow images cover at least one pixel - if (xa == xb) { - ++xb; - } - // check the scanBuf bounds - if (xa >= bw || xb < 0) { - continue; - } - if (xa < 0) { - xa = 0; - } - if (xb > bw) { - xb = bw; - } - // get the scan line - for (x = xa; x < xb; ++x) { - // map (x+0.5, y+0.5) back to the scaled image - xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + - ((SplashCoord)y + 0.5 - mat[5]) * ir10); - yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + - ((SplashCoord)y + 0.5 - mat[5]) * ir11); - // xx should always be within bounds, but floating point - // inaccuracy can cause problems - if (xx < 0) { - xx = 0; - } else if (xx >= scaledWidth) { - xx = scaledWidth - 1; - } - if (yy < 0) { - yy = 0; - } else if (yy >= scaledHeight) { - yy = scaledHeight - 1; - } - scanBuf[x] = scaledMask->data[yy * scaledWidth + xx]; - } - // clip the scan line - if (clipRes != splashClipAllInside) { - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, - state->strokeAdjust); - } - } - // draw the scan line - (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, NULL); - } - } - - delete scaledMask; -} - -// Scale an image mask into a SplashBitmap. -SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - GBool interpolate) { - SplashBitmap *dest; - - dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, - gFalse); - if (scaledHeight < srcHeight) { - if (scaledWidth < srcWidth) { - scaleMaskYdXd(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); - } else { - scaleMaskYdXu(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); - } - } else { - if (scaledWidth < srcWidth) { - scaleMaskYuXd(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); - } else { - if (interpolate) { - scaleMaskYuXuI(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); - } else { - scaleMaskYuXu(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); - } - } - } - return dest; -} - -void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf; - Guint *pixBuf; - Guint pix; - Guchar *destPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; - int i, j; - - // Bresenham parameters for y scale - yp = srcHeight / scaledHeight; - yq = srcHeight % scaledHeight; - - // Bresenham parameters for x scale - xp = srcWidth / scaledWidth; - xq = srcWidth % scaledWidth; - - // allocate buffers - lineBuf = (Guchar *)gmalloc(srcWidth); - pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - for (y = 0; y < scaledHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= scaledHeight) { - yt -= scaledHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read rows from image - memset(pixBuf, 0, srcWidth * sizeof(int)); - for (i = 0; i < yStep; ++i) { - (*src)(srcData, lineBuf); - for (j = 0; j < srcWidth; ++j) { - pixBuf[j] += lineBuf[j]; - } - } - - // init x scale Bresenham - xt = 0; - d0 = (255 << 23) / (yStep * xp); - d1 = (255 << 23) / (yStep * (xp + 1)); - - xx = 0; - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= scaledWidth) { - xt -= scaledWidth; - xStep = xp + 1; - d = d1; - } else { - xStep = xp; - d = d0; - } - - // compute the final pixel - pix = 0; - for (i = 0; i < xStep; ++i) { - pix += pixBuf[xx++]; - } - // (255 * pix) / xStep * yStep - pix = (pix * d) >> 23; - - // store the pixel - *destPtr++ = (Guchar)pix; - } - } - - gfree(pixBuf); - gfree(lineBuf); -} - -void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf; - Guint *pixBuf; - Guint pix; - Guchar *destPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; - int i, j; - - // Bresenham parameters for y scale - yp = srcHeight / scaledHeight; - yq = srcHeight % scaledHeight; - - // Bresenham parameters for x scale - xp = scaledWidth / srcWidth; - xq = scaledWidth % srcWidth; - - // allocate buffers - lineBuf = (Guchar *)gmalloc(srcWidth); - pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - for (y = 0; y < scaledHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= scaledHeight) { - yt -= scaledHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read rows from image - memset(pixBuf, 0, srcWidth * sizeof(int)); - for (i = 0; i < yStep; ++i) { - (*src)(srcData, lineBuf); - for (j = 0; j < srcWidth; ++j) { - pixBuf[j] += lineBuf[j]; - } - } - - // init x scale Bresenham - xt = 0; - d = (255 << 23) / yStep; - - for (x = 0; x < srcWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= srcWidth) { - xt -= srcWidth; - xStep = xp + 1; - } else { - xStep = xp; - } - - // compute the final pixel - pix = pixBuf[x]; - // (255 * pix) / yStep - pix = (pix * d) >> 23; - - // store the pixel - for (i = 0; i < xStep; ++i) { - *destPtr++ = (Guchar)pix; - } - } - } - - gfree(pixBuf); - gfree(lineBuf); -} - -void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf; - Guint pix; - Guchar *destPtr0, *destPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; - int i; - - // Bresenham parameters for y scale - yp = scaledHeight / srcHeight; - yq = scaledHeight % srcHeight; - - // Bresenham parameters for x scale - xp = srcWidth / scaledWidth; - xq = srcWidth % scaledWidth; - - // allocate buffers - lineBuf = (Guchar *)gmalloc(srcWidth); - - // init y scale Bresenham - yt = 0; - - destPtr0 = dest->data; - for (y = 0; y < srcHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= srcHeight) { - yt -= srcHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read row from image - (*src)(srcData, lineBuf); - - // init x scale Bresenham - xt = 0; - d0 = (255 << 23) / xp; - d1 = (255 << 23) / (xp + 1); - - xx = 0; - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= scaledWidth) { - xt -= scaledWidth; - xStep = xp + 1; - d = d1; - } else { - xStep = xp; - d = d0; - } - - // compute the final pixel - pix = 0; - for (i = 0; i < xStep; ++i) { - pix += lineBuf[xx++]; - } - // (255 * pix) / xStep - pix = (pix * d) >> 23; - - // store the pixel - for (i = 0; i < yStep; ++i) { - destPtr = destPtr0 + i * scaledWidth + x; - *destPtr = (Guchar)pix; - } - } - - destPtr0 += yStep * scaledWidth; - } - - gfree(lineBuf); -} - -void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf; - Guchar pix; - Guchar *srcPtr, *destPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep; - int i; - - // Bresenham parameters for y scale - yp = scaledHeight / srcHeight; - yq = scaledHeight % srcHeight; - - // Bresenham parameters for x scale - xp = scaledWidth / srcWidth; - xq = scaledWidth % srcWidth; - - // allocate buffers - lineBuf = (Guchar *)gmalloc(srcWidth); - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - for (y = 0; y < srcHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= srcHeight) { - yt -= srcHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read row from image - (*src)(srcData, lineBuf); - - // init x scale Bresenham - xt = 0; - - // generate one row - srcPtr = lineBuf; - for (x = 0; x < srcWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= srcWidth) { - xt -= srcWidth; - xStep = xp + 1; - } else { - xStep = xp; - } - - // compute the final pixel - pix = *srcPtr ? 255 : 0; - ++srcPtr; - - // duplicate the pixel horizontally - for (i = 0; i < xStep; ++i) { - *destPtr++ = pix; - } - } - - // duplicate the row vertically - for (i = 1 ; i < yStep; ++i) { - memcpy(destPtr, destPtr - scaledWidth, scaledWidth); - destPtr += scaledWidth; - } - } - - gfree(lineBuf); -} - -void Splash::scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf0, *lineBuf1, *tBuf; - Guchar pix; - SplashCoord yr, xr, ys, xs, ySrc, xSrc; - int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x; - Guchar *destPtr; - - // ratios - yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; - xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; - - // allocate buffers - lineBuf0 = (Guchar *)gmalloc(scaledWidth); - lineBuf1 = (Guchar *)gmalloc(scaledWidth); - - // read first two rows - (*src)(srcData, lineBuf0); - if (srcHeight > 1) { - (*src)(srcData, lineBuf1); - yBuf = 1; - } else { - memcpy(lineBuf1, lineBuf0, srcWidth); - yBuf = 0; - } - - // interpolate first two rows - for (x = scaledWidth - 1; x >= 0; --x) { - xSrc = xr * x; - xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); - xSrc1 = xSrc0 + 1; - xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); - if (xSrc0 < 0) { - xSrc0 = 0; - } - if (xSrc1 >= srcWidth) { - xSrc1 = srcWidth - 1; - } - lineBuf0[x] = (Guchar)(int) - ((xs * (int)lineBuf0[xSrc0] + - ((SplashCoord)1 - xs) * (int)lineBuf0[xSrc1]) * 255); - lineBuf1[x] = (Guchar)(int) - ((xs * (int)lineBuf1[xSrc0] + - ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1]) * 255); - } - - destPtr = dest->data; - for (y = 0; y < scaledHeight; ++y) { - - // compute vertical interpolation parameters - ySrc = yr * y; - ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); - ySrc1 = ySrc0 + 1; - ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); - if (ySrc0 < 0) { - ySrc0 = 0; - ys = 1; - } - if (ySrc1 >= srcHeight) { - ySrc1 = srcHeight - 1; - ys = 0; - } - - // read another row (if necessary) - if (ySrc1 > yBuf) { - tBuf = lineBuf0; - lineBuf0 = lineBuf1; - lineBuf1 = tBuf; - (*src)(srcData, lineBuf1); - - // interpolate the row - for (x = scaledWidth - 1; x >= 0; --x) { - xSrc = xr * x; - xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); - xSrc1 = xSrc0 + 1; - xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); - if (xSrc0 < 0) { - xSrc0 = 0; - } - if (xSrc1 >= srcWidth) { - xSrc1 = srcWidth - 1; - } - lineBuf1[x] = (Guchar)(int) - ((xs * (int)lineBuf1[xSrc0] + - ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1]) * 255); - } - - ++yBuf; - } - - // do the vertical interpolation - for (x = 0; x < scaledWidth; ++x) { - - pix = (Guchar)(int)(ys * (int)lineBuf0[x] + - ((SplashCoord)1 - ys) * (int)lineBuf1[x]); - - // store the pixel - *destPtr++ = pix; - } - } - - gfree(lineBuf1); - gfree(lineBuf0); -} - -void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, - SplashClipResult clipRes) { - SplashPipe pipe; - int w, h, x0, x1, y0, y1, y, t; - - w = src->width; - h = src->height; - pipeInit(&pipe, state->fillPattern, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - if (clipRes == splashClipAllInside) { - for (y = 0; y < h; ++y) { - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - src->data + y * (size_t)w, NULL); - } - } else { - x0 = xDest; - if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { - x0 = t; - } - x1 = xDest + w; - if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { - x1 = t; - } - y0 = yDest; - if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { - y0 = t; - } - y1 = yDest + h; - if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { - y1 = t; - } - if (x0 < x1 && y0 < y1) { - for (y = y0; y < y1; ++y) { - memcpy(scanBuf + x0, - src->data + (y - yDest) * (size_t)w + (x0 - xDest), - x1 - x0); - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, y, x0, x1 - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1, - state->strokeAdjust); - } - (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, NULL); - } - } - } -} - -SplashError Splash::drawImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat, - GBool interpolate) { - GBool ok; - SplashBitmap *scaledImg; - SplashClipResult clipRes; - GBool minorAxisZero; - SplashCoord wSize, hSize, t0, t1; - int x0, y0, x1, y1, scaledWidth, scaledHeight; - int nComps; - - if (debugMode) { - printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", - srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2], - (double)mat[3], (double)mat[4], (double)mat[5]); - } - - // check color modes - ok = gFalse; // make gcc happy - nComps = 0; // make gcc happy - switch (bitmap->mode) { - case splashModeMono1: - case splashModeMono8: - ok = srcMode == splashModeMono8; - nComps = 1; - break; - case splashModeRGB8: - case splashModeBGR8: - ok = srcMode == splashModeRGB8; - nComps = 3; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - ok = srcMode == splashModeCMYK8; - nComps = 4; - break; -#endif - default: - ok = gFalse; - break; - } - if (!ok) { - return splashErrModeMismatch; - } - - // check for singular matrix - if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { - return splashErrSingularMatrix; - } - - minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; - - // rough estimate of size of scaled image - t0 = splashAbs(mat[0]); - t1 = splashAbs(mat[1]); - wSize = t0 > t1 ? t0 : t1; - t0 = splashAbs(mat[2]); - t1 = splashAbs(mat[3]); - hSize = t0 > t1 ? t0 : t1; - - // stream-mode upscaling -- this is slower, so we only use it if the - // upscaled image is large (in which case clipping should remove - // many pixels) -#if USE_FIXEDPOINT - if ((wSize > 2 * w && hSize > 2 * h && (int)wSize > 1000000 / (int)hSize) || - (wSize > w && hSize > h && (int)wSize > 10000000 / (int)hSize) || - ((wSize > w || hSize > h) && (int)wSize > 25000000 / (int)hSize)) { -#else - if ((wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) || - (wSize > w && hSize > h && wSize * hSize > 10000000) || - ((wSize > w || hSize > h) && wSize * hSize > 25000000)) { -#endif - upscaleImage(src, srcData, srcMode, nComps, srcAlpha, - w, h, mat, interpolate); - - // scaling only - } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { - getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); - getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, - scaledWidth, scaledHeight, interpolate); - blitImage(scaledImg, srcAlpha, x0, y0, clipRes); - delete scaledImg; - } - - // scaling plus vertical flip - } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { - getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); - getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, - scaledWidth, scaledHeight, interpolate); - vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); - blitImage(scaledImg, srcAlpha, x0, y0, clipRes); - delete scaledImg; - } - - // scaling plus horizontal flip - } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) { - getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); - getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, - scaledWidth, scaledHeight, interpolate); - horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); - blitImage(scaledImg, srcAlpha, x0, y0, clipRes); - delete scaledImg; - } - - // scaling plus horizontal and vertical flips - } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) { - getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); - getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes != splashClipAllOutside) { - scaledWidth = x1 - x0; - scaledHeight = y1 - y0; - scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, - scaledWidth, scaledHeight, interpolate); - vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); - horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); - blitImage(scaledImg, srcAlpha, x0, y0, clipRes); - delete scaledImg; - } - - // all other cases - } else { - arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha, - w, h, mat, interpolate); - } - - return splashOk; -} - -void Splash::upscaleImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - SplashCoord *mat, GBool interpolate) { - SplashClipResult clipRes; - SplashPipe pipe; - SplashColorPtr unscaledImage, pixelBuf, p, q, q00, q01, q10, q11; - Guchar *unscaledAlpha, *alphaPtr; - SplashCoord xMin, yMin, xMax, yMax, t; - SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; - SplashCoord ix, iy, sx, sy, pix0, pix1; - SplashBitmapRowSize rowSize; - int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt, i; - - // compute the bbox of the target quadrilateral - xMin = xMax = mat[4]; - t = mat[2] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - t = mat[0] + mat[2] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - t = mat[0] + mat[4]; - if (t < xMin) { - xMin = t; - } else if (t > xMax) { - xMax = t; - } - getImageBounds(xMin, xMax, &xMinI, &xMaxI); - yMin = yMax = mat[5]; - t = mat[3] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - t = mat[1] + mat[3] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - t = mat[1] + mat[5]; - if (t < yMin) { - yMin = t; - } else if (t > yMax) { - yMax = t; - } - getImageBounds(yMin, yMax, &yMinI, &yMaxI); - - // clipping - clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes == splashClipAllOutside) { - return; - } - if (clipRes != splashClipAllInside) { - if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { - xMinI = tt; - } - if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { - xMaxI = tt; - } - if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { - yMinI = tt; - } - if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { - yMaxI = tt; - } - } - - // invert the matrix - det = mat[0] * mat[3] - mat[1] * mat[2]; - if (splashAbs(det) < 1e-6) { - // this should be caught by the singular matrix check in fillImageMask - return; - } - det = (SplashCoord)1 / det; - mi0 = det * mat[3] * srcWidth; - mi1 = -det * mat[1] * srcHeight; - mi2 = -det * mat[2] * srcWidth; - mi3 = det * mat[0] * srcHeight; - mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; - mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; - - // grab the image - if (srcWidth > INT_MAX / nComps) { - rowSize = -1; - } else { - rowSize = srcWidth * nComps; - } - unscaledImage = (SplashColorPtr)gmallocn64(srcHeight, rowSize); - if (srcAlpha) { - unscaledAlpha = (Guchar *)gmallocn(srcHeight, srcWidth); - for (y = 0, p = unscaledImage, alphaPtr = unscaledAlpha; - y < srcHeight; - ++y, p += rowSize, alphaPtr += srcWidth) { - (*src)(srcData, p, alphaPtr); - } - } else { - unscaledAlpha = NULL; - for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += rowSize) { - (*src)(srcData, p, NULL); - } - } - - // draw it - pixelBuf = (SplashColorPtr)gmallocn(xMaxI - xMinI, nComps); - pipeInit(&pipe, NULL, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - for (y = yMinI; y < yMaxI; ++y) { - p = pixelBuf; - for (x = xMinI; x < xMaxI; ++x) { - ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; - iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; - if (interpolate) { - if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { - x0 = splashFloor(ix - 0.5); - x1 = x0 + 1; - sx = (ix - 0.5) - x0; - y0 = splashFloor(iy - 0.5); - y1 = y0 + 1; - sy = (iy - 0.5) - y0; - if (x0 < 0) { - x0 = 0; - } - if (x1 >= srcWidth) { - x1 = srcWidth - 1; - } - if (y0 < 0) { - y0 = 0; - } - if (y1 >= srcHeight) { - y1 = srcHeight - 1; - } - q00 = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x0 * nComps]; - q01 = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x1 * nComps]; - q10 = &unscaledImage[y1 * rowSize + (SplashBitmapRowSize)x0 * nComps]; - q11 = &unscaledImage[y1 * rowSize + (SplashBitmapRowSize)x1 * nComps]; - for (i = 0; i < nComps; ++i) { - pix0 = ((SplashCoord)1 - sx) * (int)*q00++ + sx * (int)*q01++; - pix1 = ((SplashCoord)1 - sx) * (int)*q10++ + sx * (int)*q11++; - *p++ = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 - + sy * pix1); - } - if (srcAlpha) { - pix0 = ((SplashCoord)1 - sx) - * (SplashCoord)unscaledAlpha[y0 * srcWidth + x0] - + sx * (SplashCoord)unscaledAlpha[y0 * srcWidth + x1]; - pix1 = ((SplashCoord)1 - sx) - * (SplashCoord)unscaledAlpha[y1 * srcWidth + x0] - + sx * (SplashCoord)unscaledAlpha[y1 * srcWidth + x1]; - scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 - + sy * pix1); - } else { - scanBuf[x] = 0xff; - } - } else { - for (i = 0; i < nComps; ++i) { - *p++ = 0; - } - scanBuf[x] = 0; - } - } else { - x0 = splashFloor(ix); - y0 = splashFloor(iy); - if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { - q = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x0 * nComps]; - for (i = 0; i < nComps; ++i) { - *p++ = *q++; - } - if (srcAlpha) { - scanBuf[x] = unscaledAlpha[y0 * srcWidth + x0]; - } else { - scanBuf[x] = 0xff; - } - } else { - for (i = 0; i < nComps; ++i) { - *p++ = 0; - } - scanBuf[x] = 0; - } - } - } - if (clipRes != splashClipAllInside) { - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, - state->strokeAdjust); - } - } - (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, pixelBuf); - } - - gfree(pixelBuf); - gfree(unscaledImage); - gfree(unscaledAlpha); -} - -void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool interpolate) { - SplashBitmap *scaledImg; - SplashClipResult clipRes; - SplashPipe pipe; - SplashColorPtr pixelBuf; - int scaledWidth, scaledHeight, t0, t1; - SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; - SplashCoord vx[4], vy[4]; - int xMin, yMin, xMax, yMax; - ImageSection section[3]; - int nSections; - int y, xa, xb, x, i, xx, yy; - - // compute the four vertices of the target quadrilateral - vx[0] = mat[4]; vy[0] = mat[5]; - vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; - vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; - vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; - - // clipping - xMin = splashRound(vx[0]); - xMax = splashRound(vx[0]); - yMin = splashRound(vy[0]); - yMax = splashRound(vy[0]); - for (i = 1; i < 4; ++i) { - t0 = splashRound(vx[i]); - if (t0 < xMin) { - xMin = t0; - } else if (t0 > xMax) { - xMax = t0; - } - t1 = splashRound(vy[i]); - if (t1 < yMin) { - yMin = t1; - } else if (t1 > yMax) { - yMax = t1; - } - } - clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, - state->strokeAdjust); - opClipRes = clipRes; - if (clipRes == splashClipAllOutside) { - return; - } - - // compute the scale factors - if (mat[0] >= 0) { - t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); - } else { - t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); - } - if (mat[1] >= 0) { - t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); - } else { - t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); - } - scaledWidth = t0 > t1 ? t0 : t1; - if (mat[2] >= 0) { - t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); - } else { - t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); - } - if (mat[3] >= 0) { - t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); - } else { - t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); - } - scaledHeight = t0 > t1 ? t0 : t1; - if (scaledWidth == 0) { - scaledWidth = 1; - } - if (scaledHeight == 0) { - scaledHeight = 1; - } - - // compute the inverse transform (after scaling) matrix - r00 = mat[0] / scaledWidth; - r01 = mat[1] / scaledWidth; - r10 = mat[2] / scaledHeight; - r11 = mat[3] / scaledHeight; - det = r00 * r11 - r01 * r10; - if (splashAbs(det) < 1e-6) { - // this should be caught by the singular matrix check in drawImage - return; - } - ir00 = r11 / det; - ir01 = -r01 / det; - ir10 = -r10 / det; - ir11 = r00 / det; - - // scale the input image - scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, - interpolate); - - // construct the three sections - i = 0; - if (vy[1] < vy[i]) { - i = 1; - } - if (vy[2] < vy[i]) { - i = 2; - } - if (vy[3] < vy[i]) { - i = 3; - } - // NB: if using fixed point, 0.000001 will be truncated to zero, - // so these two comparisons must be <=, not < - if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && - vy[(i-1) & 3] < vy[(i+1) & 3]) { - i = (i-1) & 3; - } - if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { - section[0].y0 = splashRound(vy[i]); - section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; - if (vx[i] < vx[(i+1) & 3]) { - section[0].ia0 = i; - section[0].ia1 = (i+3) & 3; - section[0].ib0 = (i+1) & 3; - section[0].ib1 = (i+2) & 3; - } else { - section[0].ia0 = (i+1) & 3; - section[0].ia1 = (i+2) & 3; - section[0].ib0 = i; - section[0].ib1 = (i+3) & 3; - } - nSections = 1; - } else { - section[0].y0 = splashRound(vy[i]); - section[2].y1 = splashRound(vy[(i+2) & 3]) - 1; - section[0].ia0 = section[0].ib0 = i; - section[2].ia1 = section[2].ib1 = (i+2) & 3; - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[0].ia1 = section[2].ia0 = (i+1) & 3; - section[0].ib1 = section[2].ib0 = (i+3) & 3; - } else { - section[0].ia1 = section[2].ia0 = (i+3) & 3; - section[0].ib1 = section[2].ib0 = (i+1) & 3; - } - if (vy[(i+1) & 3] < vy[(i+3) & 3]) { - section[1].y0 = splashRound(vy[(i+1) & 3]); - section[2].y0 = splashRound(vy[(i+3) & 3]); - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[1].ia0 = (i+1) & 3; - section[1].ia1 = (i+2) & 3; - section[1].ib0 = i; - section[1].ib1 = (i+3) & 3; - } else { - section[1].ia0 = i; - section[1].ia1 = (i+3) & 3; - section[1].ib0 = (i+1) & 3; - section[1].ib1 = (i+2) & 3; - } - } else { - section[1].y0 = splashRound(vy[(i+3) & 3]); - section[2].y0 = splashRound(vy[(i+1) & 3]); - if (vx[(i+1) & 3] < vx[(i+3) & 3]) { - section[1].ia0 = i; - section[1].ia1 = (i+1) & 3; - section[1].ib0 = (i+3) & 3; - section[1].ib1 = (i+2) & 3; - } else { - section[1].ia0 = (i+3) & 3; - section[1].ia1 = (i+2) & 3; - section[1].ib0 = i; - section[1].ib1 = (i+1) & 3; - } - } - section[0].y1 = section[1].y0 - 1; - section[1].y1 = section[2].y0 - 1; - nSections = 3; - } - for (i = 0; i < nSections; ++i) { - section[i].xa0 = vx[section[i].ia0]; - section[i].ya0 = vy[section[i].ia0]; - section[i].xa1 = vx[section[i].ia1]; - section[i].ya1 = vy[section[i].ia1]; - section[i].xb0 = vx[section[i].ib0]; - section[i].yb0 = vy[section[i].ib0]; - section[i].xb1 = vx[section[i].ib1]; - section[i].yb1 = vy[section[i].ib1]; - section[i].dxdya = (section[i].xa1 - section[i].xa0) / - (section[i].ya1 - section[i].ya0); - section[i].dxdyb = (section[i].xb1 - section[i].xb0) / - (section[i].yb1 - section[i].yb0); - } - - // initialize the pixel pipe - pipeInit(&pipe, NULL, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - - // make sure narrow images cover at least one pixel - if (nSections == 1) { - if (section[0].y0 == section[0].y1) { - ++section[0].y1; - clipRes = opClipRes = splashClipPartial; - } - } else { - if (section[0].y0 == section[2].y1) { - ++section[1].y1; - clipRes = opClipRes = splashClipPartial; - } - } - - pixelBuf = (SplashColorPtr)gmallocn(xMax - xMin + 1, bitmapComps); - - // scan all pixels inside the target region - for (i = 0; i < nSections; ++i) { - for (y = section[i].y0; y <= section[i].y1; ++y) { - xa = splashRound(section[i].xa0 + - ((SplashCoord)y + 0.5 - section[i].ya0) * - section[i].dxdya); - xb = splashRound(section[i].xb0 + - ((SplashCoord)y + 0.5 - section[i].yb0) * - section[i].dxdyb); - if (xa > xb) { - continue; - } - // make sure narrow images cover at least one pixel - if (xa == xb) { - ++xb; - } - // check the scanBuf bounds - if (xa >= bitmap->width || xb < 0) { - continue; - } - if (xa < 0) { - xa = 0; - } - if (xb > bitmap->width) { - xb = bitmap->width; - } - // clip the scan line - memset(scanBuf + xa, 0xff, xb - xa); - if (clipRes != splashClipAllInside) { - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, y, xa, xb - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, - state->strokeAdjust); - } - } - // draw the scan line - for (x = xa; x < xb; ++x) { - // map (x+0.5, y+0.5) back to the scaled image - xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + - ((SplashCoord)y + 0.5 - mat[5]) * ir10); - yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + - ((SplashCoord)y + 0.5 - mat[5]) * ir11); - // xx should always be within bounds, but floating point - // inaccuracy can cause problems - if (xx < 0) { - xx = 0; - } else if (xx >= scaledWidth) { - xx = scaledWidth - 1; - } - if (yy < 0) { - yy = 0; - } else if (yy >= scaledHeight) { - yy = scaledHeight - 1; - } - // get the color - scaledImg->getPixel(xx, yy, pixelBuf + (x - xa) * bitmapComps); - // apply alpha - if (srcAlpha) { - scanBuf[x] = div255(scanBuf[x] * - scaledImg->alpha[yy * scaledWidth + xx]); - } - } - (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, pixelBuf); - } - } - - gfree(pixelBuf); - delete scaledImg; -} - -// Scale an image into a SplashBitmap. -SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - GBool interpolate) { - SplashBitmap *dest; - - dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha); - if (scaledHeight < srcHeight) { - if (scaledWidth < srcWidth) { - scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); - } else { - scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); - } - } else { - if (scaledWidth < srcWidth) { - scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); - } else { - if (interpolate) { - scaleImageYuXuI(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); - } else { - scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); - } - } - } - return dest; -} - -void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf, *alphaLineBuf; - Guint *pixBuf, *alphaPixBuf; - Guint pix0, pix1, pix2; -#if SPLASH_CMYK - Guint pix3; -#endif - Guint alpha; - Guchar *destPtr, *destAlphaPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; - int i, j; - - // Bresenham parameters for y scale - yp = srcHeight / scaledHeight; - yq = srcHeight % scaledHeight; - - // Bresenham parameters for x scale - xp = srcWidth / scaledWidth; - xq = srcWidth % scaledWidth; - - // allocate buffers - lineBuf = (Guchar *)gmallocn(srcWidth, nComps); - pixBuf = (Guint *)gmallocn(srcWidth, (int)(nComps * sizeof(int))); - if (srcAlpha) { - alphaLineBuf = (Guchar *)gmalloc(srcWidth); - alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); - } else { - alphaLineBuf = NULL; - alphaPixBuf = NULL; - } - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - destAlphaPtr = dest->alpha; - for (y = 0; y < scaledHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= scaledHeight) { - yt -= scaledHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read rows from image - memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); - if (srcAlpha) { - memset(alphaPixBuf, 0, srcWidth * sizeof(int)); - } - for (i = 0; i < yStep; ++i) { - (*src)(srcData, lineBuf, alphaLineBuf); - for (j = 0; j < srcWidth * nComps; ++j) { - pixBuf[j] += lineBuf[j]; - } - if (srcAlpha) { - for (j = 0; j < srcWidth; ++j) { - alphaPixBuf[j] += alphaLineBuf[j]; - } - } - } - - // init x scale Bresenham - xt = 0; - d0 = (1 << 23) / (yStep * xp); - d1 = (1 << 23) / (yStep * (xp + 1)); - - xx = xxa = 0; - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= scaledWidth) { - xt -= scaledWidth; - xStep = xp + 1; - d = d1; - } else { - xStep = xp; - d = d0; - } - - switch (srcMode) { - - case splashModeMono8: - - // compute the final pixel - pix0 = 0; - for (i = 0; i < xStep; ++i) { - pix0 += pixBuf[xx++]; - } - // pix / xStep * yStep - pix0 = (pix0 * d) >> 23; - - // store the pixel - *destPtr++ = (Guchar)pix0; - break; - - case splashModeRGB8: - - // compute the final pixel - pix0 = pix1 = pix2 = 0; - for (i = 0; i < xStep; ++i) { - pix0 += pixBuf[xx]; - pix1 += pixBuf[xx+1]; - pix2 += pixBuf[xx+2]; - xx += 3; - } - // pix / xStep * yStep - pix0 = (pix0 * d) >> 23; - pix1 = (pix1 * d) >> 23; - pix2 = (pix2 * d) >> 23; - - // store the pixel - *destPtr++ = (Guchar)pix0; - *destPtr++ = (Guchar)pix1; - *destPtr++ = (Guchar)pix2; - break; - -#if SPLASH_CMYK - case splashModeCMYK8: - - // compute the final pixel - pix0 = pix1 = pix2 = pix3 = 0; - for (i = 0; i < xStep; ++i) { - pix0 += pixBuf[xx]; - pix1 += pixBuf[xx+1]; - pix2 += pixBuf[xx+2]; - pix3 += pixBuf[xx+3]; - xx += 4; - } - // pix / xStep * yStep - pix0 = (pix0 * d) >> 23; - pix1 = (pix1 * d) >> 23; - pix2 = (pix2 * d) >> 23; - pix3 = (pix3 * d) >> 23; - - // store the pixel - *destPtr++ = (Guchar)pix0; - *destPtr++ = (Guchar)pix1; - *destPtr++ = (Guchar)pix2; - *destPtr++ = (Guchar)pix3; - break; -#endif - - - case splashModeMono1: // mono1 is not allowed - case splashModeBGR8: // bgr8 is not allowed - default: - break; - } - - // process alpha - if (srcAlpha) { - alpha = 0; - for (i = 0; i < xStep; ++i, ++xxa) { - alpha += alphaPixBuf[xxa]; - } - // alpha / xStep * yStep - alpha = (alpha * d) >> 23; - *destAlphaPtr++ = (Guchar)alpha; - } - } - } - - gfree(alphaPixBuf); - gfree(alphaLineBuf); - gfree(pixBuf); - gfree(lineBuf); -} - -void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf, *alphaLineBuf; - Guint *pixBuf, *alphaPixBuf; - Guint pix[splashMaxColorComps]; - Guint alpha; - Guchar *destPtr, *destAlphaPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; - int i, j; - - // Bresenham parameters for y scale - yp = srcHeight / scaledHeight; - yq = srcHeight % scaledHeight; - - // Bresenham parameters for x scale - xp = scaledWidth / srcWidth; - xq = scaledWidth % srcWidth; - - // allocate buffers - lineBuf = (Guchar *)gmallocn(srcWidth, nComps); - pixBuf = (Guint *)gmallocn(srcWidth, (int)(nComps * sizeof(int))); - if (srcAlpha) { - alphaLineBuf = (Guchar *)gmalloc(srcWidth); - alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); - } else { - alphaLineBuf = NULL; - alphaPixBuf = NULL; - } - - // make gcc happy - pix[0] = pix[1] = pix[2] = 0; -#if SPLASH_CMYK - pix[3] = 0; -#endif - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - destAlphaPtr = dest->alpha; - for (y = 0; y < scaledHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= scaledHeight) { - yt -= scaledHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read rows from image - memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); - if (srcAlpha) { - memset(alphaPixBuf, 0, srcWidth * sizeof(int)); - } - for (i = 0; i < yStep; ++i) { - (*src)(srcData, lineBuf, alphaLineBuf); - for (j = 0; j < srcWidth * nComps; ++j) { - pixBuf[j] += lineBuf[j]; - } - if (srcAlpha) { - for (j = 0; j < srcWidth; ++j) { - alphaPixBuf[j] += alphaLineBuf[j]; - } - } - } - - // init x scale Bresenham - xt = 0; - d = (1 << 23) / yStep; - - for (x = 0; x < srcWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= srcWidth) { - xt -= srcWidth; - xStep = xp + 1; - } else { - xStep = xp; - } - - // compute the final pixel - for (i = 0; i < nComps; ++i) { - // pixBuf[] / yStep - pix[i] = (pixBuf[x * nComps + i] * d) >> 23; - } - - // store the pixel - switch (srcMode) { - case splashModeMono8: - for (i = 0; i < xStep; ++i) { - *destPtr++ = (Guchar)pix[0]; - } - break; - case splashModeRGB8: - for (i = 0; i < xStep; ++i) { - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[2]; - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - for (i = 0; i < xStep; ++i) { - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[2]; - *destPtr++ = (Guchar)pix[3]; - } - break; -#endif - case splashModeMono1: // mono1 is not allowed - case splashModeBGR8: // BGR8 is not allowed - default: - break; - } - - // process alpha - if (srcAlpha) { - // alphaPixBuf[] / yStep - alpha = (alphaPixBuf[x] * d) >> 23; - for (i = 0; i < xStep; ++i) { - *destAlphaPtr++ = (Guchar)alpha; - } - } - } - } - - gfree(alphaPixBuf); - gfree(alphaLineBuf); - gfree(pixBuf); - gfree(lineBuf); -} - -void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf, *alphaLineBuf; - Guint pix[splashMaxColorComps]; - Guint alpha; - Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; - int i, j; - - // Bresenham parameters for y scale - yp = scaledHeight / srcHeight; - yq = scaledHeight % srcHeight; - - // Bresenham parameters for x scale - xp = srcWidth / scaledWidth; - xq = srcWidth % scaledWidth; - - // allocate buffers - lineBuf = (Guchar *)gmallocn(srcWidth, nComps); - if (srcAlpha) { - alphaLineBuf = (Guchar *)gmalloc(srcWidth); - } else { - alphaLineBuf = NULL; - } - - // make gcc happy - pix[0] = pix[1] = pix[2] = 0; -#if SPLASH_CMYK - pix[3] = 0; -#endif - - // init y scale Bresenham - yt = 0; - - destPtr0 = dest->data; - destAlphaPtr0 = dest->alpha; - for (y = 0; y < srcHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= srcHeight) { - yt -= srcHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read row from image - (*src)(srcData, lineBuf, alphaLineBuf); - - // init x scale Bresenham - xt = 0; - d0 = (1 << 23) / xp; - d1 = (1 << 23) / (xp + 1); - - xx = xxa = 0; - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= scaledWidth) { - xt -= scaledWidth; - xStep = xp + 1; - d = d1; - } else { - xStep = xp; - d = d0; - } - - // compute the final pixel - for (i = 0; i < nComps; ++i) { - pix[i] = 0; - } - for (i = 0; i < xStep; ++i) { - for (j = 0; j < nComps; ++j, ++xx) { - pix[j] += lineBuf[xx]; - } - } - for (i = 0; i < nComps; ++i) { - // pix[] / xStep - pix[i] = (pix[i] * d) >> 23; - } - - // store the pixel - switch (srcMode) { - case splashModeMono8: - for (i = 0; i < yStep; ++i) { - destPtr = destPtr0 + (i * scaledWidth + x) * nComps; - *destPtr++ = (Guchar)pix[0]; - } - break; - case splashModeRGB8: - for (i = 0; i < yStep; ++i) { - destPtr = destPtr0 + (i * scaledWidth + x) * nComps; - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[2]; - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - for (i = 0; i < yStep; ++i) { - destPtr = destPtr0 + (i * scaledWidth + x) * nComps; - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[2]; - *destPtr++ = (Guchar)pix[3]; - } - break; -#endif - case splashModeMono1: // mono1 is not allowed - case splashModeBGR8: // BGR8 is not allowed - default: - break; - } - - // process alpha - if (srcAlpha) { - alpha = 0; - for (i = 0; i < xStep; ++i, ++xxa) { - alpha += alphaLineBuf[xxa]; - } - // alpha / xStep - alpha = (alpha * d) >> 23; - for (i = 0; i < yStep; ++i) { - destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; - *destAlphaPtr = (Guchar)alpha; - } - } - } - - destPtr0 += yStep * scaledWidth * nComps; - if (srcAlpha) { - destAlphaPtr0 += yStep * scaledWidth; - } - } - - gfree(alphaLineBuf); - gfree(lineBuf); -} - -void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf, *alphaLineBuf; - Guchar pix0, pix1, pix2; -#if SPLASH_CMYK - Guchar pix3; -#endif - Guchar alpha; - Guchar *srcPtr, *srcAlphaPtr; - Guchar *destPtr, *destAlphaPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep; - int i; - - // Bresenham parameters for y scale - yp = scaledHeight / srcHeight; - yq = scaledHeight % srcHeight; - - // Bresenham parameters for x scale - xp = scaledWidth / srcWidth; - xq = scaledWidth % srcWidth; - - // allocate buffers - lineBuf = (Guchar *)gmallocn(srcWidth, nComps); - if (srcAlpha) { - alphaLineBuf = (Guchar *)gmalloc(srcWidth); - } else { - alphaLineBuf = NULL; - } - - // init y scale Bresenham - yt = 0; - - destPtr = dest->data; - destAlphaPtr = dest->alpha; - for (y = 0; y < srcHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= srcHeight) { - yt -= srcHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read row from image - (*src)(srcData, lineBuf, alphaLineBuf); - - // init x scale Bresenham - xt = 0; - - // generate one row - srcPtr = lineBuf; - srcAlphaPtr = alphaLineBuf; - for (x = 0; x < srcWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= srcWidth) { - xt -= srcWidth; - xStep = xp + 1; - } else { - xStep = xp; - } - - // duplicate the pixel horizontally - switch (srcMode) { - case splashModeMono8: - pix0 = *srcPtr++; - for (i = 0; i < xStep; ++i) { - *destPtr++ = pix0; - } - break; - case splashModeRGB8: - pix0 = *srcPtr++; - pix1 = *srcPtr++; - pix2 = *srcPtr++; - for (i = 0; i < xStep; ++i) { - *destPtr++ = pix0; - *destPtr++ = pix1; - *destPtr++ = pix2; - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - pix0 = *srcPtr++; - pix1 = *srcPtr++; - pix2 = *srcPtr++; - pix3 = *srcPtr++; - for (i = 0; i < xStep; ++i) { - *destPtr++ = pix0; - *destPtr++ = pix1; - *destPtr++ = pix2; - *destPtr++ = pix3; - } - break; -#endif - case splashModeMono1: // mono1 is not allowed - case splashModeBGR8: // BGR8 is not allowed - default: - break; - } - - // duplicate the alpha value horizontally - if (srcAlpha) { - alpha = *srcAlphaPtr++; - for (i = 0; i < xStep; ++i) { - *destAlphaPtr++ = alpha; - } - } - } - - // duplicate the row vertically - for (i = 1; i < yStep; ++i) { - memcpy(destPtr, destPtr - scaledWidth * nComps, - scaledWidth * nComps); - destPtr += scaledWidth * nComps; - } - if (srcAlpha) { - for (i = 1; i < yStep; ++i) { - memcpy(destAlphaPtr, destAlphaPtr - scaledWidth, scaledWidth); - destAlphaPtr += scaledWidth; - } - } - } - - gfree(alphaLineBuf); - gfree(lineBuf); -} - -void Splash::scaleImageYuXuI(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest) { - Guchar *lineBuf0, *lineBuf1, *alphaLineBuf0, *alphaLineBuf1, *tBuf; - Guchar pix[splashMaxColorComps]; - SplashCoord yr, xr, ys, xs, ySrc, xSrc; - int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x, i; - Guchar *destPtr, *destAlphaPtr; - - // ratios - yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; - xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; - - // allocate buffers - lineBuf0 = (Guchar *)gmallocn(scaledWidth, nComps); - lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps); - if (srcAlpha) { - alphaLineBuf0 = (Guchar *)gmalloc(scaledWidth); - alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth); - } else { - alphaLineBuf0 = NULL; - alphaLineBuf1 = NULL; - } - - // read first two rows - (*src)(srcData, lineBuf0, alphaLineBuf0); - if (srcHeight > 1) { - (*src)(srcData, lineBuf1, alphaLineBuf1); - yBuf = 1; - } else { - memcpy(lineBuf1, lineBuf0, srcWidth * nComps); - if (srcAlpha) { - memcpy(alphaLineBuf1, alphaLineBuf0, srcWidth); - } - yBuf = 0; - } - - // interpolate first two rows - for (x = scaledWidth - 1; x >= 0; --x) { - xSrc = xr * x; - xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); - xSrc1 = xSrc0 + 1; - xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); - if (xSrc0 < 0) { - xSrc0 = 0; - } - if (xSrc1 >= srcWidth) { - xSrc1 = srcWidth - 1; - } - for (i = 0; i < nComps; ++i) { - lineBuf0[x*nComps+i] = (Guchar)(int) - (xs * (int)lineBuf0[xSrc0*nComps+i] + - ((SplashCoord)1 - xs) * (int)lineBuf0[xSrc1*nComps+i]); - lineBuf1[x*nComps+i] = (Guchar)(int) - (xs * (int)lineBuf1[xSrc0*nComps+i] + - ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1*nComps+i]); - } - if (srcAlpha) { - alphaLineBuf0[x] = (Guchar)(int) - (xs * (int)alphaLineBuf0[xSrc0] + - ((SplashCoord)1 - xs) * (int)alphaLineBuf0[xSrc1]); - alphaLineBuf1[x] = (Guchar)(int) - (xs * (int)alphaLineBuf1[xSrc0] + - ((SplashCoord)1 - xs) * (int)alphaLineBuf1[xSrc1]); - } - } - - // make gcc happy - pix[0] = pix[1] = pix[2] = 0; -#if SPLASH_CMYK - pix[3] = 0; -#endif - - destPtr = dest->data; - destAlphaPtr = dest->alpha; - for (y = 0; y < scaledHeight; ++y) { - - // compute vertical interpolation parameters - ySrc = yr * y; - ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); - ySrc1 = ySrc0 + 1; - ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); - if (ySrc0 < 0) { - ySrc0 = 0; - ys = 1; - } - if (ySrc1 >= srcHeight) { - ySrc1 = srcHeight - 1; - ys = 0; - } - - // read another row (if necessary) - if (ySrc1 > yBuf) { - tBuf = lineBuf0; - lineBuf0 = lineBuf1; - lineBuf1 = tBuf; - tBuf = alphaLineBuf0; - alphaLineBuf0 = alphaLineBuf1; - alphaLineBuf1 = tBuf; - (*src)(srcData, lineBuf1, alphaLineBuf1); - - // interpolate the row - for (x = scaledWidth - 1; x >= 0; --x) { - xSrc = xr * x; - xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); - xSrc1 = xSrc0 + 1; - xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); - if (xSrc0 < 0) { - xSrc0 = 0; - } - if (xSrc1 >= srcWidth) { - xSrc1 = srcWidth - 1; - } - for (i = 0; i < nComps; ++i) { - lineBuf1[x*nComps+i] = (Guchar)(int) - (xs * (int)lineBuf1[xSrc0*nComps+i] + - ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1*nComps+i]); - } - if (srcAlpha) { - alphaLineBuf1[x] = (Guchar)(int) - (xs * (int)alphaLineBuf1[xSrc0] + - ((SplashCoord)1 - xs) * (int)alphaLineBuf1[xSrc1]); - } - } - - ++yBuf; - } - - // do the vertical interpolation - for (x = 0; x < scaledWidth; ++x) { - - for (i = 0; i < nComps; ++i) { - pix[i] = (Guchar)(int) - (ys * (int)lineBuf0[x*nComps+i] + - ((SplashCoord)1 - ys) * (int)lineBuf1[x*nComps+i]); - } - - // store the pixel - switch (srcMode) { - case splashModeMono8: - *destPtr++ = pix[0]; - break; - case splashModeRGB8: - *destPtr++ = pix[0]; - *destPtr++ = pix[1]; - *destPtr++ = pix[2]; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - *destPtr++ = pix[0]; - *destPtr++ = pix[1]; - *destPtr++ = pix[2]; - *destPtr++ = pix[3]; - break; -#endif - case splashModeMono1: // mono1 is not allowed - case splashModeBGR8: // BGR8 is not allowed - default: - break; - } - - // process alpha - if (srcAlpha) { - *destAlphaPtr++ = (Guchar)(int) - (ys * (int)alphaLineBuf0[x] + - ((SplashCoord)1 - ys) * (int)alphaLineBuf1[x]); - } - } - } - - gfree(alphaLineBuf1); - gfree(alphaLineBuf0); - gfree(lineBuf1); - gfree(lineBuf0); -} - -void Splash::vertFlipImage(SplashBitmap *img, int width, int height, - int nComps) { - Guchar *lineBuf; - Guchar *p0, *p1; - int w; - - w = width * nComps; - lineBuf = (Guchar *)gmalloc(w); - for (p0 = img->data, p1 = img->data + (height - 1) * (size_t)w; - p0 < p1; - p0 += w, p1 -= w) { - memcpy(lineBuf, p0, w); - memcpy(p0, p1, w); - memcpy(p1, lineBuf, w); - } - if (img->alpha) { - for (p0 = img->alpha, p1 = img->alpha + (height - 1) * (size_t)width; - p0 < p1; - p0 += width, p1 -= width) { - memcpy(lineBuf, p0, width); - memcpy(p0, p1, width); - memcpy(p1, lineBuf, width); - } - } - gfree(lineBuf); -} - -void Splash::horizFlipImage(SplashBitmap *img, int width, int height, - int nComps) { - Guchar *lineBuf; - SplashColorPtr p0, p1, p2; - int w, x, y, i; - - w = width * nComps; - lineBuf = (Guchar *)gmalloc(w); - for (y = 0, p0 = img->data; y < height; ++y, p0 += img->rowSize) { - memcpy(lineBuf, p0, w); - p1 = p0; - p2 = lineBuf + (w - nComps); - for (x = 0; x < width; ++x) { - for (i = 0; i < nComps; ++i) { - p1[i] = p2[i]; - } - p1 += nComps; - p2 -= nComps; - } - } - if (img->alpha) { - for (y = 0, p0 = img->alpha; y < height; ++y, p0 += width) { - memcpy(lineBuf, p0, width); - p1 = p0; - p2 = lineBuf + (width - 1); - for (x = 0; x < width; ++x) { - *p1++ = *p2--; - } - } - } - gfree(lineBuf); -} - -void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, - SplashClipResult clipRes) { - SplashPipe pipe; - int w, h, x0, y0, x1, y1, y; - - // split the image into clipped and unclipped regions - w = src->width; - h = src->height; - if (clipRes == splashClipAllInside) { - x0 = 0; - y0 = 0; - x1 = w; - y1 = h; - } else { - if (state->clip->getNumPaths()) { - x0 = x1 = w; - y0 = y1 = h; - } else { - if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) { - x0 = 0; - } - if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) { - y0 = 0; - } - if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) { - x1 = w; - } - if (x1 < x0) { - x1 = x0; - } - if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) { - y1 = h; - } - if (y1 < y0) { - y1 = y0; - } - } - } - - // draw the unclipped region - if (x0 < w && y0 < h && x0 < x1 && y0 < y1) { - pipeInit(&pipe, NULL, - (Guchar)splashRound(state->fillAlpha * 255), - srcAlpha, gFalse); - if (srcAlpha) { - for (y = y0; y < y1; ++y) { - (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, - src->alpha + y * src->alphaRowSize + x0, - src->data + y * src->rowSize + x0 * bitmapComps); - } - } else { - for (y = y0; y < y1; ++y) { - (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, - NULL, - src->data + y * src->getRowSize() + - x0 * bitmapComps); - } - } - } - - // draw the clipped regions - if (y0 > 0) { - blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0); - } - if (y1 < h) { - blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1); - } - if (x0 > 0 && y0 < y1) { - blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0); - } - if (x1 < w && y0 < y1) { - blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0, - w - x1, y1 - y0); - } -} - -void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha, - int xSrc, int ySrc, int xDest, int yDest, - int w, int h) { - SplashPipe pipe; - int y; - - if (xDest < 0) { - xSrc -= xDest; - w += xDest; - xDest = 0; - } - if (xDest + w > bitmap->width) { - w = bitmap->width - xDest; - } - if (yDest < 0) { - ySrc -= yDest; - h += yDest; - yDest = 0; - } - if (yDest + h > bitmap->height) { - h = bitmap->height - yDest; - } - if (w <= 0 || h <= 0) { - return; - } - - pipeInit(&pipe, NULL, - (Guchar)splashRound(state->fillAlpha * 255), - gTrue, gFalse); - if (srcAlpha) { - for (y = 0; y < h; ++y) { - memcpy(scanBuf + xDest, - src->alpha + (ySrc + y) * src->alphaRowSize + xSrc, - w); - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, - state->strokeAdjust); - } - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - scanBuf + xDest, - src->data + (ySrc + y) * src->rowSize + - xSrc * bitmapComps); - } - } else { - for (y = 0; y < h; ++y) { - memset(scanBuf + xDest, 0xff, w); - if (vectorAntialias) { - state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, - state->strokeAdjust); - } else { - state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, - state->strokeAdjust); - } - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - scanBuf + xDest, - src->data + (ySrc + y) * src->rowSize + - xSrc * bitmapComps); - } - } -} - -SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, - int xDest, int yDest, int w, int h, - GBool noClip, GBool nonIsolated) { - SplashPipe pipe; - Guchar *mono1Ptr, *lineBuf, *linePtr; - Guchar mono1Mask, b; - int x0, x1, x, y0, y1, y, t; - - if (!(src->mode == bitmap->mode || - (src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) || - (src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) { - return splashErrModeMismatch; - } - - pipeInit(&pipe, NULL, - (Guchar)splashRound(state->fillAlpha * 255), - !noClip || src->alpha != NULL, nonIsolated); - if (src->mode == splashModeMono1) { - // in mono1 mode, pipeRun expects the source to be in mono8 - // format, so we need to extract the source color values into - // scanBuf, expanding them from mono1 to mono8 - if (noClip) { - if (src->alpha) { - for (y = 0; y < h; ++y) { - mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3); - mono1Mask = (Guchar)(0x80 >> (xSrc & 7)); - for (x = 0; x < w; ++x) { - scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; - mono1Ptr += mono1Mask & 1; - mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); - } - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - src->alpha + - (ySrc + y) * src->alphaRowSize + xSrc, - scanBuf); - } - } else { - for (y = 0; y < h; ++y) { - mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3); - mono1Mask = (Guchar)(0x80 >> (xSrc & 7)); - for (x = 0; x < w; ++x) { - scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; - mono1Ptr += mono1Mask & 1; - mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); - } - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - NULL, - scanBuf); - } - } - } else { - x0 = xDest; - if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { - x0 = t; - } - x1 = xDest + w; - if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { - x1 = t; - } - y0 = yDest; - if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { - y0 = t; - } - y1 = yDest + h; - if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { - y1 = t; - } - if (x0 < x1 && y0 < y1) { - if (src->alpha) { - for (y = y0; y < y1; ++y) { - mono1Ptr = src->data - + (ySrc + y - yDest) * src->rowSize - + ((xSrc + x0 - xDest) >> 3); - mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7)); - for (x = x0; x < x1; ++x) { - scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; - mono1Ptr += mono1Mask & 1; - mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); - } - memcpy(scanBuf2 + x0, - src->alpha + (ySrc + y - yDest) * src->alphaRowSize + - (xSrc + x0 - xDest), - x1 - x0); - if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1, - state->strokeAdjust)) { - continue; - } - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, x0, x1 - 1, y, - scanBuf2 + x0, - scanBuf + x0); - } - } else { - for (y = y0; y < y1; ++y) { - mono1Ptr = src->data - + (ySrc + y - yDest) * src->rowSize - + ((xSrc + x0 - xDest) >> 3); - mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7)); - for (x = x0; x < x1; ++x) { - scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; - mono1Ptr += mono1Mask & 1; - mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); - } - memset(scanBuf2 + x0, 0xff, x1 - x0); - if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1, - state->strokeAdjust)) { - continue; - } - (this->*pipe.run)(&pipe, x0, x1 - 1, y, - scanBuf2 + x0, - scanBuf + x0); - } - } - } - } - - } else if (src->mode == splashModeBGR8) { - // in BGR8 mode, pipeRun expects the source to be in RGB8 format, - // so we need to swap bytes - lineBuf = (Guchar *)gmallocn(w, 3); - if (noClip) { - if (src->alpha) { - for (y = 0; y < h; ++y) { - memcpy(lineBuf, - src->data + (ySrc + y) * src->rowSize + xSrc * 3, - w * 3); - for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) { - b = linePtr[0]; - linePtr[0] = linePtr[2]; - linePtr[2] = b; - } - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - src->alpha + - (ySrc + y) * src->alphaRowSize + xSrc, - lineBuf); - } - } else { - for (y = 0; y < h; ++y) { - memcpy(lineBuf, - src->data + (ySrc + y) * src->rowSize + xSrc * 3, - w * 3); - for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) { - b = linePtr[0]; - linePtr[0] = linePtr[2]; - linePtr[2] = b; - } - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - NULL, lineBuf); - } - } - } else { - x0 = xDest; - if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { - x0 = t; - } - x1 = xDest + w; - if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { - x1 = t; - } - y0 = yDest; - if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { - y0 = t; - } - y1 = yDest + h; - if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { - y1 = t; - } - if (x0 < x1 && y0 < y1) { - if (src->alpha) { - for (y = y0; y < y1; ++y) { - memcpy(scanBuf + x0, - src->alpha + (ySrc + y - yDest) * src->alphaRowSize + - (xSrc + x0 - xDest), - x1 - x0); - state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); - memcpy(lineBuf, - src->data + - (ySrc + y - yDest) * src->rowSize + - (xSrc + x0 - xDest) * 3, - (x1 - x0) * 3); - for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) { - b = linePtr[0]; - linePtr[0] = linePtr[2]; - linePtr[2] = b; - } - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, x0, x1 - 1, y, - scanBuf + x0, lineBuf); - } - } else { - for (y = y0; y < y1; ++y) { - memset(scanBuf + x0, 0xff, x1 - x0); - state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); - memcpy(lineBuf, - src->data + - (ySrc + y - yDest) * src->rowSize + - (xSrc + x0 - xDest) * 3, - (x1 - x0) * 3); - for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) { - b = linePtr[0]; - linePtr[0] = linePtr[2]; - linePtr[2] = b; - } - (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y, - scanBuf + x0, - src->data + - (ySrc + y - yDest) * src->rowSize + - (xSrc + x0 - xDest) * bitmapComps); - } - } - } - } - gfree(lineBuf); - - } else { // src->mode not mono1 or BGR8 - if (noClip) { - if (src->alpha) { - for (y = 0; y < h; ++y) { - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - src->alpha + - (ySrc + y) * src->alphaRowSize + xSrc, - src->data + (ySrc + y) * src->rowSize + - xSrc * bitmapComps); - } - } else { - for (y = 0; y < h; ++y) { - (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, - NULL, - src->data + (ySrc + y) * src->rowSize + - xSrc * bitmapComps); - } - } - } else { - x0 = xDest; - if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { - x0 = t; - } - x1 = xDest + w; - if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { - x1 = t; - } - y0 = yDest; - if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { - y0 = t; - } - y1 = yDest + h; - if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { - y1 = t; - } - if (x0 < x1 && y0 < y1) { - if (src->alpha) { - for (y = y0; y < y1; ++y) { - memcpy(scanBuf + x0, - src->alpha + (ySrc + y - yDest) * src->alphaRowSize + - (xSrc + x0 - xDest), - x1 - x0); - state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - (this->*pipe.run)(&pipe, x0, x1 - 1, y, - scanBuf + x0, - src->data + - (ySrc + y - yDest) * src->rowSize + - (xSrc + x0 - xDest) * bitmapComps); - } - } else { - for (y = y0; y < y1; ++y) { - memset(scanBuf + x0, 0xff, x1 - x0); - state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); - (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y, - scanBuf + x0, - src->data + - (ySrc + y - yDest) * src->rowSize + - (xSrc + x0 - xDest) * bitmapComps); - } - } - } - } - } - - return splashOk; -} - -void Splash::compositeBackground(SplashColorPtr color) { - SplashColorPtr p; - Guchar *q; - Guchar alpha, alpha1, c, color0, color1, color2, mask; -#if SPLASH_CMYK - Guchar color3; -#endif - int x, y; - - switch (bitmap->mode) { - case splashModeMono1: - color0 = color[0]; - for (y = 0; y < bitmap->height; ++y) { - p = &bitmap->data[y * bitmap->rowSize]; - q = &bitmap->alpha[y * bitmap->alphaRowSize]; - mask = 0x80; - for (x = 0; x < bitmap->width; ++x) { - alpha = *q++; - if (alpha == 0) { - if (color0 & 0x80) { - *p |= mask; - } else { - *p &= (Guchar)~mask; - } - } else if (alpha != 255) { - alpha1 = (Guchar)(255 - alpha); - c = (*p & mask) ? 0xff : 0x00; - c = div255(alpha1 * color0 + alpha * c); - if (c & 0x80) { - *p |= mask; - } else { - *p &= (Guchar)~mask; - } - } - if (!(mask = (Guchar)(mask >> 1))) { - mask = 0x80; - ++p; - } - } - } - break; - case splashModeMono8: - color0 = color[0]; - for (y = 0; y < bitmap->height; ++y) { - p = &bitmap->data[y * bitmap->rowSize]; - q = &bitmap->alpha[y * bitmap->alphaRowSize]; - for (x = 0; x < bitmap->width; ++x) { - alpha = *q++; - if (alpha == 0) { - p[0] = color0; - } else if (alpha != 255) { - alpha1 = (Guchar)(255 - alpha); - p[0] = div255(alpha1 * color0 + alpha * p[0]); - } - ++p; - } - } - break; - case splashModeRGB8: - case splashModeBGR8: - color0 = color[0]; - color1 = color[1]; - color2 = color[2]; - for (y = 0; y < bitmap->height; ++y) { - p = &bitmap->data[y * bitmap->rowSize]; - q = &bitmap->alpha[y * bitmap->alphaRowSize]; - for (x = 0; x < bitmap->width; ++x) { - alpha = *q++; - if (alpha == 0) { - p[0] = color0; - p[1] = color1; - p[2] = color2; - } else if (alpha != 255) { - alpha1 = (Guchar)(255 - alpha); - p[0] = div255(alpha1 * color0 + alpha * p[0]); - p[1] = div255(alpha1 * color1 + alpha * p[1]); - p[2] = div255(alpha1 * color2 + alpha * p[2]); - } - p += 3; - } - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - color0 = color[0]; - color1 = color[1]; - color2 = color[2]; - color3 = color[3]; - for (y = 0; y < bitmap->height; ++y) { - p = &bitmap->data[y * bitmap->rowSize]; - q = &bitmap->alpha[y * bitmap->alphaRowSize]; - for (x = 0; x < bitmap->width; ++x) { - alpha = *q++; - if (alpha == 0) { - p[0] = color0; - p[1] = color1; - p[2] = color2; - p[3] = color3; - } else if (alpha != 255) { - alpha1 = (Guchar)(255 - alpha); - p[0] = div255(alpha1 * color0 + alpha * p[0]); - p[1] = div255(alpha1 * color1 + alpha * p[1]); - p[2] = div255(alpha1 * color2 + alpha * p[2]); - p[3] = div255(alpha1 * color3 + alpha * p[3]); - } - p += 4; - } - } - break; -#endif - } - memset(bitmap->alpha, 255, bitmap->alphaRowSize * bitmap->height); -} - -SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, - int xDest, int yDest, int w, int h) { - SplashColorPtr p, q; - Guchar mask, srcMask; - int x, y; - - if (src->mode != bitmap->mode) { - return splashErrModeMismatch; - } - - switch (bitmap->mode) { - case splashModeMono1: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; - mask = (Guchar)(0x80 >> (xDest & 7)); - q = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)]; - srcMask = (Guchar)(0x80 >> (xSrc & 7)); - for (x = 0; x < w; ++x) { - if (*q & srcMask) { - *p |= mask; - } else { - *p &= (Guchar)~mask; - } - if (!(mask = (Guchar)(mask >> 1))) { - mask = 0x80; - ++p; - } - if (!(srcMask = (Guchar)(srcMask >> 1))) { - srcMask = 0x80; - ++q; - } - } - } - break; - case splashModeMono8: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; - q = &src->data[(ySrc + y) * src->rowSize + xSrc]; - memcpy(p, q, w); - } - break; - case splashModeRGB8: - case splashModeBGR8: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; - q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc]; - memcpy(p, q, 3 * w); - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; - q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; - memcpy(p, q, 4 * w); - } - break; -#endif - } - - if (bitmap->alpha) { - for (y = 0; y < h; ++y) { - q = &bitmap->alpha[(yDest + y) * bitmap->alphaRowSize + xDest]; - memset(q, 0, w); - } - } - - return splashOk; -} - -SplashError Splash::blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc, - int xDest, int yDest, int w, int h) { - SplashColorPtr p, q; - Guchar *alpha0Ptr; - Guchar alpha0, aSrc, mask, srcMask; - int x, y; - - if (bitmap->mode != dest->mode || - !bitmap->alpha || - !dest->alpha || - !groupBackBitmap) { - return splashErrModeMismatch; - } - - switch (bitmap->mode) { - case splashModeMono1: - for (y = 0; y < h; ++y) { - p = &dest->data[(yDest + y) * dest->rowSize + (xDest >> 3)]; - mask = (Guchar)(0x80 >> (xDest & 7)); - q = &bitmap->data[(ySrc + y) * bitmap->rowSize + (xSrc >> 3)]; - srcMask = (Guchar)(0x80 >> (xSrc & 7)); - for (x = 0; x < w; ++x) { - if (*q & srcMask) { - *p |= mask; - } else { - *p &= (Guchar)~mask; - } - if (!(mask = (Guchar)(mask >> 1))) { - mask = 0x80; - ++p; - } - if (!(srcMask = (Guchar)(srcMask >> 1))) { - srcMask = 0x80; - ++q; - } - } - } - break; - case splashModeMono8: - for (y = 0; y < h; ++y) { - p = &dest->data[(yDest + y) * dest->rowSize + xDest]; - q = &bitmap->data[(ySrc + y) * bitmap->rowSize + xSrc]; - memcpy(p, q, w); - } - break; - case splashModeRGB8: - case splashModeBGR8: - for (y = 0; y < h; ++y) { - p = &dest->data[(yDest + y) * dest->rowSize + 3 * xDest]; - q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 3 * xSrc]; - memcpy(p, q, 3 * w); - } - break; -#if SPLASH_CMYK - case splashModeCMYK8: - for (y = 0; y < h; ++y) { - p = &dest->data[(yDest + y) * dest->rowSize + 4 * xDest]; - q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 4 * xSrc]; - memcpy(p, q, 4 * w); - } - break; -#endif - } - - for (y = 0; y < h; ++y) { - p = &dest->alpha[(yDest + y) * dest->alphaRowSize + xDest]; - q = &bitmap->alpha[(ySrc + y) * bitmap->alphaRowSize + xSrc]; - alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + ySrc + y) - * groupBackBitmap->alphaRowSize + - (groupBackX + xSrc)]; - for (x = 0; x < w; ++x) { - alpha0 = *alpha0Ptr++; - aSrc = *q++; - *p++ = (Guchar)(alpha0 + aSrc - div255(alpha0 * aSrc)); - } - } - - return splashOk; -} - -SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w, - int lineCap, int lineJoin, - GBool flatten) { - SplashPath *pathIn, *dashPath, *pathOut; - SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; - SplashCoord crossprod, dotprod, miter, m; - SplashCoord angle, angleNext, dAngle, xc, yc; - SplashCoord dxJoin, dyJoin, dJoin, kappa; - SplashCoord cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4; - GBool first, last, closed; - int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1; - int left0, left1, left2, right0, right1, right2, join0, join1, join2; - int leftFirst, rightFirst, firstPt; - - pathOut = new SplashPath(); - - if (path->length == 0) { - return pathOut; - } - - if (flatten) { - pathIn = flattenPath(path, state->matrix, state->flatness); - if (state->lineDashLength > 0) { - dashPath = makeDashedPath(pathIn); - delete pathIn; - pathIn = dashPath; - if (pathIn->length == 0) { - delete pathIn; - return pathOut; - } - } - } else { - pathIn = path; - } - - subpathStart0 = subpathStart1 = 0; // make gcc happy - seg = 0; // make gcc happy - closed = gFalse; // make gcc happy - left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy - leftFirst = rightFirst = firstPt = 0; // make gcc happy - - i0 = 0; - for (i1 = i0; - !(pathIn->flags[i1] & splashPathLast) && - i1 + 1 < pathIn->length && - pathIn->pts[i1+1].x == pathIn->pts[i1].x && - pathIn->pts[i1+1].y == pathIn->pts[i1].y; - ++i1) ; - - while (i1 < pathIn->length) { - if ((first = pathIn->flags[i0] & splashPathFirst)) { - subpathStart0 = i0; - subpathStart1 = i1; - seg = 0; - closed = pathIn->flags[i0] & splashPathClosed; - } - j0 = i1 + 1; - if (j0 < pathIn->length) { - for (j1 = j0; - !(pathIn->flags[j1] & splashPathLast) && - j1 + 1 < pathIn->length && - pathIn->pts[j1+1].x == pathIn->pts[j1].x && - pathIn->pts[j1+1].y == pathIn->pts[j1].y; - ++j1) ; - } else { - j1 = j0; - } - if (pathIn->flags[i1] & splashPathLast) { - if (first && lineCap == splashLineCapRound) { - // special case: zero-length subpath with round line caps --> - // draw a circle - pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, - pathIn->pts[i0].y); - pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, - pathIn->pts[i0].y + bezierCircle2 * w, - pathIn->pts[i0].x + bezierCircle2 * w, - pathIn->pts[i0].y + (SplashCoord)0.5 * w, - pathIn->pts[i0].x, - pathIn->pts[i0].y + (SplashCoord)0.5 * w); - pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w, - pathIn->pts[i0].y + (SplashCoord)0.5 * w, - pathIn->pts[i0].x - (SplashCoord)0.5 * w, - pathIn->pts[i0].y + bezierCircle2 * w, - pathIn->pts[i0].x - (SplashCoord)0.5 * w, - pathIn->pts[i0].y); - pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w, - pathIn->pts[i0].y - bezierCircle2 * w, - pathIn->pts[i0].x - bezierCircle2 * w, - pathIn->pts[i0].y - (SplashCoord)0.5 * w, - pathIn->pts[i0].x, - pathIn->pts[i0].y - (SplashCoord)0.5 * w); - pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w, - pathIn->pts[i0].y - (SplashCoord)0.5 * w, - pathIn->pts[i0].x + (SplashCoord)0.5 * w, - pathIn->pts[i0].y - bezierCircle2 * w, - pathIn->pts[i0].x + (SplashCoord)0.5 * w, - pathIn->pts[i0].y); - pathOut->close(); - } - i0 = j0; - i1 = j1; - continue; - } - last = pathIn->flags[j1] & splashPathLast; - if (last) { - k0 = subpathStart1 + 1; - } else { - k0 = j1 + 1; - } - for (k1 = k0; - !(pathIn->flags[k1] & splashPathLast) && - k1 + 1 < pathIn->length && - pathIn->pts[k1+1].x == pathIn->pts[k1].x && - pathIn->pts[k1+1].y == pathIn->pts[k1].y; - ++k1) ; - - // compute the deltas for segment (i1, j0) -#if USE_FIXEDPOINT - // the 1/d value can be small, which introduces significant - // inaccuracies in fixed point mode - d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, - pathIn->pts[j0].x, pathIn->pts[j0].y); - dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d; - dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d; -#else - d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, - pathIn->pts[j0].x, pathIn->pts[j0].y); - dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x); - dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y); -#endif - wdx = (SplashCoord)0.5 * w * dx; - wdy = (SplashCoord)0.5 * w * dy; - - // draw the start cap - if (i0 == subpathStart0) { - firstPt = pathOut->length; - } - if (first && !closed) { - switch (lineCap) { - case splashLineCapButt: - pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); - pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); - break; - case splashLineCapRound: - pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); - pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx, - pathIn->pts[i0].y + wdx - bezierCircle * wdy, - pathIn->pts[i0].x - wdx - bezierCircle * wdy, - pathIn->pts[i0].y - wdy + bezierCircle * wdx, - pathIn->pts[i0].x - wdx, - pathIn->pts[i0].y - wdy); - pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy, - pathIn->pts[i0].y - wdy - bezierCircle * wdx, - pathIn->pts[i0].x + wdy - bezierCircle * wdx, - pathIn->pts[i0].y - wdx - bezierCircle * wdy, - pathIn->pts[i0].x + wdy, - pathIn->pts[i0].y - wdx); - break; - case splashLineCapProjecting: - pathOut->moveTo(pathIn->pts[i0].x - wdx - wdy, - pathIn->pts[i0].y + wdx - wdy); - pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy, - pathIn->pts[i0].y - wdx - wdy); - break; - } - } else { - pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); - pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); - } - - // draw the left side of the segment rectangle and the end cap - left2 = pathOut->length - 1; - if (last && !closed) { - switch (lineCap) { - case splashLineCapButt: - pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); - pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); - break; - case splashLineCapRound: - pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); - pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx, - pathIn->pts[j0].y - wdx + bezierCircle * wdy, - pathIn->pts[j0].x + wdx + bezierCircle * wdy, - pathIn->pts[j0].y + wdy - bezierCircle * wdx, - pathIn->pts[j0].x + wdx, - pathIn->pts[j0].y + wdy); - pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy, - pathIn->pts[j0].y + wdy + bezierCircle * wdx, - pathIn->pts[j0].x - wdy + bezierCircle * wdx, - pathIn->pts[j0].y + wdx + bezierCircle * wdy, - pathIn->pts[j0].x - wdy, - pathIn->pts[j0].y + wdx); - break; - case splashLineCapProjecting: - pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx, - pathIn->pts[j0].y - wdx + wdy); - pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx, - pathIn->pts[j0].y + wdx + wdy); - break; - } - } else { - pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); - pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); - } - - // draw the right side of the segment rectangle - // (NB: if stroke adjustment is enabled, the closepath operation MUST - // add a segment because this segment is used for a hint) - right2 = pathOut->length - 1; - pathOut->close(state->strokeAdjust != splashStrokeAdjustOff); - - // draw the join - join2 = pathOut->length; - if (!last || closed) { - - // compute the deltas for segment (j1, k0) -#if USE_FIXEDPOINT - // the 1/d value can be small, which introduces significant - // inaccuracies in fixed point mode - d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, - pathIn->pts[k0].x, pathIn->pts[k0].y); - dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d; - dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d; -#else - d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, - pathIn->pts[k0].x, pathIn->pts[k0].y); - dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x); - dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y); -#endif - wdxNext = (SplashCoord)0.5 * w * dxNext; - wdyNext = (SplashCoord)0.5 * w * dyNext; - - // compute the join parameters - crossprod = dx * dyNext - dy * dxNext; - dotprod = -(dx * dxNext + dy * dyNext); - if (dotprod > 0.9999) { - // avoid a divide-by-zero -- set miter to something arbitrary - // such that sqrt(miter) will exceed miterLimit (and m is never - // used in that situation) - // (note: the comparison value (0.9999) has to be less than - // 1-epsilon, where epsilon is the smallest value - // representable in the fixed point format) - miter = (state->miterLimit + 1) * (state->miterLimit + 1); - m = 0; - } else { - miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod); - if (miter < 1) { - // this can happen because of floating point inaccuracies - miter = 1; - } - m = splashSqrt(miter - 1); - } - - // round join - if (lineJoin == splashLineJoinRound) { - // join angle < 180 - if (crossprod < 0) { - angle = atan2((double)dx, (double)-dy); - angleNext = atan2((double)dxNext, (double)-dyNext); - if (angle < angleNext) { - angle += 2 * M_PI; - } - dAngle = (angle - angleNext) / M_PI; - if (dAngle < 0.501) { - // span angle is <= 90 degrees -> draw a single arc - kappa = dAngle * bezierCircle * w; - cx1 = pathIn->pts[j0].x - wdy + kappa * dx; - cy1 = pathIn->pts[j0].y + wdx + kappa * dy; - cx2 = pathIn->pts[j0].x - wdyNext - kappa * dxNext; - cy2 = pathIn->pts[j0].y + wdxNext - kappa * dyNext; - pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); - pathOut->lineTo(pathIn->pts[j0].x - wdyNext, - pathIn->pts[j0].y + wdxNext); - pathOut->curveTo(cx2, cy2, cx1, cy1, - pathIn->pts[j0].x - wdy, - pathIn->pts[j0].y + wdx); - } else { - // span angle is > 90 degrees -> split into two arcs - dJoin = splashDist(-wdy, wdx, -wdyNext, wdxNext); - if (dJoin > 0) { - dxJoin = (-wdyNext + wdy) / dJoin; - dyJoin = (wdxNext - wdx) / dJoin; - xc = pathIn->pts[j0].x - + (SplashCoord)0.5 * w - * cos((double)((SplashCoord)0.5 * (angle + angleNext))); - yc = pathIn->pts[j0].y - + (SplashCoord)0.5 * w - * sin((double)((SplashCoord)0.5 * (angle + angleNext))); - kappa = dAngle * bezierCircle2 * w; - cx1 = pathIn->pts[j0].x - wdy + kappa * dx; - cy1 = pathIn->pts[j0].y + wdx + kappa * dy; - cx2 = xc - kappa * dxJoin; - cy2 = yc - kappa * dyJoin; - cx3 = xc + kappa * dxJoin; - cy3 = yc + kappa * dyJoin; - cx4 = pathIn->pts[j0].x - wdyNext - kappa * dxNext; - cy4 = pathIn->pts[j0].y + wdxNext - kappa * dyNext; - pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); - pathOut->lineTo(pathIn->pts[j0].x - wdyNext, - pathIn->pts[j0].y + wdxNext); - pathOut->curveTo(cx4, cy4, cx3, cy3, xc, yc); - pathOut->curveTo(cx2, cy2, cx1, cy1, - pathIn->pts[j0].x - wdy, - pathIn->pts[j0].y + wdx); - } - } - - // join angle >= 180 - } else { - angle = atan2((double)-dx, (double)dy); - angleNext = atan2((double)-dxNext, (double)dyNext); - if (angleNext < angle) { - angleNext += 2 * M_PI; - } - dAngle = (angleNext - angle) / M_PI; - if (dAngle < 0.501) { - // span angle is <= 90 degrees -> draw a single arc - kappa = dAngle * bezierCircle * w; - cx1 = pathIn->pts[j0].x + wdy + kappa * dx; - cy1 = pathIn->pts[j0].y - wdx + kappa * dy; - cx2 = pathIn->pts[j0].x + wdyNext - kappa * dxNext; - cy2 = pathIn->pts[j0].y - wdxNext - kappa * dyNext; - pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); - pathOut->lineTo(pathIn->pts[j0].x + wdy, - pathIn->pts[j0].y - wdx); - pathOut->curveTo(cx1, cy1, cx2, cy2, - pathIn->pts[j0].x + wdyNext, - pathIn->pts[j0].y - wdxNext); - } else { - // span angle is > 90 degrees -> split into two arcs - dJoin = splashDist(wdy, -wdx, wdyNext, -wdxNext); - if (dJoin > 0) { - dxJoin = (wdyNext - wdy) / dJoin; - dyJoin = (-wdxNext + wdx) / dJoin; - xc = pathIn->pts[j0].x - + (SplashCoord)0.5 * w - * cos((double)((SplashCoord)0.5 * (angle + angleNext))); - yc = pathIn->pts[j0].y - + (SplashCoord)0.5 * w - * sin((double)((SplashCoord)0.5 * (angle + angleNext))); - kappa = dAngle * bezierCircle2 * w; - cx1 = pathIn->pts[j0].x + wdy + kappa * dx; - cy1 = pathIn->pts[j0].y - wdx + kappa * dy; - cx2 = xc - kappa * dxJoin; - cy2 = yc - kappa * dyJoin; - cx3 = xc + kappa * dxJoin; - cy3 = yc + kappa * dyJoin; - cx4 = pathIn->pts[j0].x + wdyNext - kappa * dxNext; - cy4 = pathIn->pts[j0].y - wdxNext - kappa * dyNext; - pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); - pathOut->lineTo(pathIn->pts[j0].x + wdy, - pathIn->pts[j0].y - wdx); - pathOut->curveTo(cx1, cy1, cx2, cy2, xc, yc); - pathOut->curveTo(cx3, cy3, cx4, cy4, - pathIn->pts[j0].x + wdyNext, - pathIn->pts[j0].y - wdxNext); - } - } - } - - } else { - pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); - - // join angle < 180 - if (crossprod < 0) { - pathOut->lineTo(pathIn->pts[j0].x - wdyNext, - pathIn->pts[j0].y + wdxNext); - // miter join inside limit - if (lineJoin == splashLineJoinMiter && - splashSqrt(miter) <= state->miterLimit) { - pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m, - pathIn->pts[j0].y + wdx + wdy * m); - pathOut->lineTo(pathIn->pts[j0].x - wdy, - pathIn->pts[j0].y + wdx); - // bevel join or miter join outside limit - } else { - pathOut->lineTo(pathIn->pts[j0].x - wdy, - pathIn->pts[j0].y + wdx); - } - - // join angle >= 180 - } else { - pathOut->lineTo(pathIn->pts[j0].x + wdy, - pathIn->pts[j0].y - wdx); - // miter join inside limit - if (lineJoin == splashLineJoinMiter && - splashSqrt(miter) <= state->miterLimit) { - pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m, - pathIn->pts[j0].y - wdx + wdy * m); - pathOut->lineTo(pathIn->pts[j0].x + wdyNext, - pathIn->pts[j0].y - wdxNext); - // bevel join or miter join outside limit - } else { - pathOut->lineTo(pathIn->pts[j0].x + wdyNext, - pathIn->pts[j0].y - wdxNext); - } - } - } - - pathOut->close(); - } - - // add stroke adjustment hints - if (state->strokeAdjust != splashStrokeAdjustOff) { - - // subpath with one segment - if (seg == 0 && last) { - switch (lineCap) { - case splashLineCapButt: - pathOut->addStrokeAdjustHint(firstPt, left2 + 1, - firstPt, pathOut->length - 1); - break; - case splashLineCapProjecting: - pathOut->addStrokeAdjustHint(firstPt, left2 + 1, - firstPt, pathOut->length - 1, gTrue); - break; - case splashLineCapRound: - break; - } - pathOut->addStrokeAdjustHint(left2, right2, - firstPt, pathOut->length - 1); - } else { - - // start of subpath - if (seg == 1) { - - // start cap - if (!closed) { - switch (lineCap) { - case splashLineCapButt: - pathOut->addStrokeAdjustHint(firstPt, left1 + 1, - firstPt, firstPt + 1); - pathOut->addStrokeAdjustHint(firstPt, left1 + 1, - right1 + 1, right1 + 1); - break; - case splashLineCapProjecting: - pathOut->addStrokeAdjustHint(firstPt, left1 + 1, - firstPt, firstPt + 1, gTrue); - pathOut->addStrokeAdjustHint(firstPt, left1 + 1, - right1 + 1, right1 + 1, gTrue); - break; - case splashLineCapRound: - break; - } - } - - // first segment - pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2); - pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); - } - - // middle of subpath - if (seg > 1) { - pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); - pathOut->addStrokeAdjustHint(left1, right1, join0, left2); - pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); - } - - // end of subpath - if (last) { - - if (closed) { - // first segment - pathOut->addStrokeAdjustHint(leftFirst, rightFirst, - left2 + 1, right2); - pathOut->addStrokeAdjustHint(leftFirst, rightFirst, - join2, pathOut->length - 1); - - // last segment - pathOut->addStrokeAdjustHint(left2, right2, - left1 + 1, right1); - pathOut->addStrokeAdjustHint(left2, right2, - join1, pathOut->length - 1); - pathOut->addStrokeAdjustHint(left2, right2, - leftFirst - 1, leftFirst); - pathOut->addStrokeAdjustHint(left2, right2, - rightFirst + 1, rightFirst + 1); - - } else { - - // last segment - pathOut->addStrokeAdjustHint(left2, right2, - left1 + 1, right1); - pathOut->addStrokeAdjustHint(left2, right2, - join1, pathOut->length - 1); - - // end cap - switch (lineCap) { - case splashLineCapButt: - pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1, - left2 + 1, left2 + 2); - break; - case splashLineCapProjecting: - pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1, - left2 + 1, left2 + 2, gTrue); - break; - case splashLineCapRound: - break; - } - } - } - } - - left0 = left1; - left1 = left2; - right0 = right1; - right1 = right2; - join0 = join1; - join1 = join2; - if (seg == 0) { - leftFirst = left2; - rightFirst = right2; - } - } - - i0 = j0; - i1 = j1; - ++seg; - } - - if (pathIn != path) { - delete pathIn; - } - - return pathOut; -} - -SplashClipResult Splash::limitRectToClipRect(int *xMin, int *yMin, - int *xMax, int *yMax) { - int t; - - if ((t = state->clip->getXMinI(state->strokeAdjust)) > *xMin) { - *xMin = t; - } - if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < *xMax) { - *xMax = t; - } - if ((t = state->clip->getYMinI(state->strokeAdjust)) > *yMin) { - *yMin = t; - } - if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < *yMax) { - *yMax = t; - } - if (*xMin >= *xMax || *yMin >= *yMax) { - return splashClipAllOutside; - } - return state->clip->testRect(*xMin, *yMin, *xMax - 1, *yMax - 1, - state->strokeAdjust); -} - -void Splash::dumpPath(SplashPath *path) { - int i; - - for (i = 0; i < path->length; ++i) { - printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n", - i, (double)path->pts[i].x, (double)path->pts[i].y, - (path->flags[i] & splashPathFirst) ? " first" : "", - (path->flags[i] & splashPathLast) ? " last" : "", - (path->flags[i] & splashPathClosed) ? " closed" : "", - (path->flags[i] & splashPathCurve) ? " curve" : ""); - } - if (path->hintsLength == 0) { - printf(" no hints\n"); - } else { - for (i = 0; i < path->hintsLength; ++i) { - printf(" hint %3d: ctrl0=%d ctrl1=%d pts=%d..%d\n", - i, path->hints[i].ctrl0, path->hints[i].ctrl1, - path->hints[i].firstPt, path->hints[i].lastPt); - } - } -} - -void Splash::dumpXPath(SplashXPath *path) { - int i; - - for (i = 0; i < path->length; ++i) { - printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f count=%d\n", - i, (double)path->segs[i].x0, (double)path->segs[i].y0, - (double)path->segs[i].x1, (double)path->segs[i].y1, - path->segs[i].count); - } -} - diff --git a/test/bug-hunting/cve/CVE-2019-10024/Splash.h b/test/bug-hunting/cve/CVE-2019-10024/Splash.h deleted file mode 100644 index dc667512119..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10024/Splash.h +++ /dev/null @@ -1,449 +0,0 @@ -//======================================================================== -// -// Splash.h -// -// Copyright 2003-2013 Glyph & Cog, LLC -// -//======================================================================== - -#ifndef SPLASH_H -#define SPLASH_H - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "SplashTypes.h" -#include "SplashClip.h" - -class Splash; -class SplashBitmap; -struct SplashGlyphBitmap; -class SplashState; -class SplashPattern; -class SplashScreen; -class SplashPath; -class SplashXPath; -class SplashFont; -struct SplashPipe; - -//------------------------------------------------------------------------ - -// Retrieves the next line of pixels in an image mask. Normally, -// fills in * and returns true. If the image stream is -// exhausted, returns false. -typedef GBool (*SplashImageMaskSource)(void *data, Guchar *pixel); - -// Retrieves the next line of pixels in an image. Normally, fills in -// * and returns true. If the image stream is exhausted, -// returns false. -typedef GBool (*SplashImageSource)(void *data, SplashColorPtr colorLine, - Guchar *alphaLine); - - -//------------------------------------------------------------------------ - -enum SplashPipeResultColorCtrl { - splashPipeResultColorNoAlphaBlendMono, - splashPipeResultColorNoAlphaBlendRGB, -#if SPLASH_CMYK - splashPipeResultColorNoAlphaBlendCMYK, -#endif - splashPipeResultColorAlphaNoBlendMono, - splashPipeResultColorAlphaNoBlendRGB, -#if SPLASH_CMYK - splashPipeResultColorAlphaNoBlendCMYK, -#endif - splashPipeResultColorAlphaBlendMono, - splashPipeResultColorAlphaBlendRGB -#if SPLASH_CMYK - , - splashPipeResultColorAlphaBlendCMYK -#endif -}; - -//------------------------------------------------------------------------ -// Splash -//------------------------------------------------------------------------ - -class Splash { -public: - - // Create a new rasterizer object. - Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, - SplashScreenParams *screenParams = NULL); - Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, - SplashScreen *screenA); - - ~Splash(); - - //----- state read - - SplashCoord *getMatrix(); - SplashPattern *getStrokePattern(); - SplashPattern *getFillPattern(); - SplashScreen *getScreen(); - SplashBlendFunc getBlendFunc(); - SplashCoord getStrokeAlpha(); - SplashCoord getFillAlpha(); - SplashCoord getLineWidth(); - int getLineCap(); - int getLineJoin(); - SplashCoord getMiterLimit(); - SplashCoord getFlatness(); - SplashCoord *getLineDash(); - int getLineDashLength(); - SplashCoord getLineDashPhase(); - SplashStrokeAdjustMode getStrokeAdjust(); - SplashClip *getClip(); - SplashBitmap *getSoftMask(); - GBool getInNonIsolatedGroup(); - GBool getInKnockoutGroup(); - - //----- state write - - void setMatrix(SplashCoord *matrix); - void setStrokePattern(SplashPattern *strokeColor); - void setFillPattern(SplashPattern *fillColor); - void setScreen(SplashScreen *screen); - void setBlendFunc(SplashBlendFunc func); - void setStrokeAlpha(SplashCoord alpha); - void setFillAlpha(SplashCoord alpha); - void setLineWidth(SplashCoord lineWidth); - void setLineCap(int lineCap); - void setLineJoin(int lineJoin); - void setMiterLimit(SplashCoord miterLimit); - void setFlatness(SplashCoord flatness); - // the array will be copied - void setLineDash(SplashCoord *lineDash, int lineDashLength, - SplashCoord lineDashPhase); - void setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust); - // NB: uses transformed coordinates. - void clipResetToRect(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1); - // NB: uses transformed coordinates. - SplashError clipToRect(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1); - // NB: uses untransformed coordinates. - SplashError clipToPath(SplashPath *path, GBool eo); - void setSoftMask(SplashBitmap *softMask); - void setInTransparencyGroup(SplashBitmap *groupBackBitmapA, - int groupBackXA, int groupBackYA, - GBool nonIsolated, GBool knockout); - void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray); - void setOverprintMask(Guint overprintMask); - void setEnablePathSimplification(GBool en); - - //----- state save/restore - - void saveState(); - SplashError restoreState(); - - //----- drawing operations - - // Fill the bitmap with . This is not subject to clipping. - void clear(SplashColorPtr color, Guchar alpha = 0x00); - - // Stroke a path using the current stroke pattern. - SplashError stroke(SplashPath *path); - - // Fill a path using the current fill pattern. - SplashError fill(SplashPath *path, GBool eo); - - // Draw a character, using the current fill pattern. - SplashError fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font); - - // Draw a glyph, using the current fill pattern. This function does - // not free any data, i.e., it ignores glyph->freeData. - SplashError fillGlyph(SplashCoord x, SplashCoord y, - SplashGlyphBitmap *glyph); - - // Draws an image mask using the fill color. This will read - // lines of pixels from , starting with the top line. "1" - // pixels will be drawn with the current fill color; "0" pixels are - // transparent. The matrix: - // [ mat[0] mat[1] 0 ] - // [ mat[2] mat[3] 0 ] - // [ mat[4] mat[5] 1 ] - // maps a unit square to the desired destination for the image, in - // PostScript style: - // [x' y' 1] = [x y 1] * mat - // Note that the Splash y axis points downward, and the image source - // is assumed to produce pixels in raster order, starting from the - // top line. - SplashError fillImageMask(SplashImageMaskSource src, void *srcData, - int w, int h, SplashCoord *mat, - GBool glyphMode, GBool interpolate); - - // Draw an image. This will read lines of pixels from - // , starting with the top line. These pixels are assumed to - // be in the source mode, . If is true, the - // alpha values returned by are used; otherwise they are - // ignored. The following combinations of source and target modes - // are supported: - // source target - // ------ ------ - // Mono8 Mono1 -- with dithering - // Mono8 Mono8 - // RGB8 RGB8 - // BGR8 RGB8 - // CMYK8 CMYK8 - // The matrix behaves as for fillImageMask. - SplashError drawImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat, - GBool interpolate); - - // Composite a rectangular region from onto this Splash - // object. - SplashError composite(SplashBitmap *src, int xSrc, int ySrc, - int xDest, int yDest, int w, int h, - GBool noClip, GBool nonIsolated); - - // Composite this Splash object onto a background color. The - // background alpha is assumed to be 1. - void compositeBackground(SplashColorPtr color); - - // Copy a rectangular region from onto the bitmap belonging to - // this Splash object. The destination alpha values are all set to - // zero. - SplashError blitTransparent(SplashBitmap *src, int xSrc, int ySrc, - int xDest, int yDest, int w, int h); - - // Copy a rectangular region from the bitmap belonging to this - // Splash object to . The alpha values are corrected for a - // non-isolated group. - SplashError blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc, - int xDest, int yDest, int w, int h); - - //----- misc - - // Construct a path for a stroke, given the path to be stroked and - // the line width . All other stroke parameters are taken from - // the current state. If is true, this function will - // first flatten the path and handle the linedash. - SplashPath *makeStrokePath(SplashPath *path, SplashCoord w, - int lineCap, int lineJoin, - GBool flatten = gTrue); - - // Reduce the size of a rectangle as much as possible by moving any - // edges that are completely outside the clip region. Returns the - // clipping status of the resulting rectangle. - SplashClipResult limitRectToClipRect(int *xMin, int *yMin, - int *xMax, int *yMax); - - // Return the associated bitmap. - SplashBitmap *getBitmap() { - return bitmap; - } - - // Set the minimum line width. - void setMinLineWidth(SplashCoord w) { - minLineWidth = w; - } - - // Get a bounding box which includes all modifications since the - // last call to clearModRegion. - void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax) - { - *xMin = modXMin; *yMin = modYMin; *xMax = modXMax; *yMax = modYMax; - } - - // Clear the modified region bounding box. - void clearModRegion(); - - // Get clipping status for the last drawing operation subject to - // clipping. - SplashClipResult getClipRes() { - return opClipRes; - } - - // Toggle debug mode on or off. - void setDebugMode(GBool debugModeA) { - debugMode = debugModeA; - } - -#if 1 //~tmp: turn off anti-aliasing temporarily - void setInShading(GBool sh) { - inShading = sh; - } -#endif - - -private: - - void pipeInit(SplashPipe *pipe, SplashPattern *pattern, - Guchar aInput, GBool usesShape, - GBool nonIsolatedGroup); - void pipeRun(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#if SPLASH_CMYK - void pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#endif - void pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#if SPLASH_CMYK - void pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#endif - void pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); - void pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#if SPLASH_CMYK - void pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, - Guchar *shapePtr, SplashColorPtr cSrcPtr); -#endif - void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, - SplashCoord *xo, SplashCoord *yo); - void updateModX(int x); - void updateModY(int y); - void strokeNarrow(SplashPath *path); - void drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip); - void strokeWide(SplashPath *path, SplashCoord w, - int lineCap, int lineJoin); - SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness); - void flattenCurve(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1, - SplashCoord x2, SplashCoord y2, - SplashCoord x3, SplashCoord y3, - SplashCoord *matrix, SplashCoord flatness2, - SplashPath *fPath); - SplashPath *makeDashedPath(SplashPath *xPath); - SplashError fillWithPattern(SplashPath *path, GBool eo, - SplashPattern *pattern, SplashCoord alpha); - SplashPath *tweakFillPath(SplashPath *path); - GBool pathAllOutside(SplashPath *path); - SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph); - void getImageBounds(SplashCoord xyMin, SplashCoord xyMax, - int *xyMinI, int *xyMaxI); - void upscaleMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool glyphMode, - GBool interpolate); - void arbitraryTransformMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool glyphMode, - GBool interpolate); - SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - GBool interpolate); - void scaleMaskYdXd(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleMaskYdXu(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleMaskYuXd(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleMaskYuXu(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, - int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void blitMask(SplashBitmap *src, int xDest, int yDest, - SplashClipResult clipRes); - void upscaleImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - SplashCoord *mat, GBool interpolate); - void arbitraryTransformImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, - int srcWidth, int srcHeight, - SplashCoord *mat, GBool interpolate); - SplashBitmap *scaleImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - GBool interpolate); - void scaleImageYdXd(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleImageYdXu(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleImageYuXd(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleImageYuXu(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void scaleImageYuXuI(SplashImageSource src, void *srcData, - SplashColorMode srcMode, int nComps, - GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight, - SplashBitmap *dest); - void vertFlipImage(SplashBitmap *img, int width, int height, - int nComps); - void horizFlipImage(SplashBitmap *img, int width, int height, - int nComps); - void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, - SplashClipResult clipRes); - void blitImageClipped(SplashBitmap *src, GBool srcAlpha, - int xSrc, int ySrc, int xDest, int yDest, - int w, int h); - void dumpPath(SplashPath *path); - void dumpXPath(SplashXPath *path); - - - static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[]; - static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[]; - static SplashPipeResultColorCtrl pipeResultColorAlphaBlend[]; - static int pipeNonIsoGroupCorrection[]; - - SplashBitmap *bitmap; - int bitmapComps; - SplashState *state; - Guchar *scanBuf; - Guchar *scanBuf2; - SplashBitmap // for transparency groups, this is the bitmap - *groupBackBitmap; // containing the alpha0/color0 values - int groupBackX, groupBackY; // offset within groupBackBitmap - SplashCoord minLineWidth; - int modXMin, modYMin, modXMax, modYMax; - SplashClipResult opClipRes; - GBool vectorAntialias; - GBool inShading; - GBool debugMode; -}; - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-10024/expected.txt b/test/bug-hunting/cve/CVE-2019-10024/expected.txt deleted file mode 100644 index 187d24aec0a..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10024/expected.txt +++ /dev/null @@ -1,3 +0,0 @@ -Splash.cc:5555:bughuntingDivByZero -Splash.cc:5556:bughuntingDivByZero - diff --git a/test/bug-hunting/cve/CVE-2019-10025/Stream.cc b/test/bug-hunting/cve/CVE-2019-10025/Stream.cc deleted file mode 100644 index 4e9a2dc2ff4..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10025/Stream.cc +++ /dev/null @@ -1,5849 +0,0 @@ -//======================================================================== -// -// Stream.cc -// -// Copyright 1996-2003 Glyph & Cog, LLC -// -//======================================================================== - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include -#include -#include -#include -#ifdef _WIN32 -#include -#else -#include -#endif -#include -#include -#include "gmem.h" -#include "gmempp.h" -#include "gfile.h" -#if MULTITHREADED -#include "GMutex.h" -#endif -#include "config.h" -#include "Error.h" -#include "Object.h" -#include "Lexer.h" -#include "GfxState.h" -#include "Stream.h" -#include "JBIG2Stream.h" -#include "JPXStream.h" -#include "Stream-CCITT.h" - -#ifdef __DJGPP__ -static GBool setDJSYSFLAGS = gFalse; -#endif - -#ifdef VMS -#ifdef __GNUC__ -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 -#endif -#endif - -//------------------------------------------------------------------------ -// Stream (base class) -//------------------------------------------------------------------------ - -Stream::Stream() { -} - -Stream::~Stream() { -} - -void Stream::close() { -} - -int Stream::getRawChar() { - error(errInternal, -1, "Called getRawChar() on non-predictor stream"); - return EOF; -} - -int Stream::getBlock(char *buf, int size) { - int n, c; - - n = 0; - while (n < size) { - if ((c = getChar()) == EOF) { - break; - } - buf[n++] = (char)c; - } - return n; -} - -char *Stream::getLine(char *buf, int size) { - int i; - int c; - - if (lookChar() == EOF || size < 0) - return NULL; - for (i = 0; i < size - 1; ++i) { - c = getChar(); - if (c == EOF || c == '\n') - break; - if (c == '\r') { - if ((c = lookChar()) == '\n') - getChar(); - break; - } - buf[i] = (char)c; - } - buf[i] = '\0'; - return buf; -} - -Guint Stream::discardChars(Guint n) { - char buf[4096]; - Guint count, i, j; - - count = 0; - while (count < n) { - if ((i = n - count) > sizeof(buf)) { - i = (Guint)sizeof(buf); - } - j = (Guint)getBlock(buf, (int)i); - count += j; - if (j != i) { - break; - } - } - return count; -} - -GString *Stream::getPSFilter(int psLevel, const char *indent) { - return new GString(); -} - -Stream *Stream::addFilters(Object *dict, int recursion) { - Object obj, obj2; - Object params, params2; - Stream *str; - int i; - - str = this; - dict->dictLookup("Filter", &obj); - if (obj.isNull()) { - obj.free(); - dict->dictLookup("F", &obj); - } - dict->dictLookup("DecodeParms", ¶ms); - if (params.isNull()) { - params.free(); - dict->dictLookup("DP", ¶ms); - } - if (obj.isName()) { - str = makeFilter(obj.getName(), str, ¶ms, recursion); - } else if (obj.isArray()) { - for (i = 0; i < obj.arrayGetLength(); ++i) { - obj.arrayGet(i, &obj2, recursion); - if (params.isArray()) - params.arrayGet(i, ¶ms2, recursion); - else - params2.initNull(); - if (obj2.isName()) { - str = makeFilter(obj2.getName(), str, ¶ms2, recursion); - } else { - error(errSyntaxError, getPos(), "Bad filter name"); - str = new EOFStream(str); - } - obj2.free(); - params2.free(); - } - } else if (!obj.isNull()) { - error(errSyntaxError, getPos(), "Bad 'Filter' attribute in stream"); - } - obj.free(); - params.free(); - - return str; -} - -Stream *Stream::makeFilter(char *name, Stream *str, Object *params, - int recursion) { - int pred; // parameters - int colors; - int bits; - int early; - int encoding; - GBool endOfLine, byteAlign, endOfBlock, black; - int columns, rows; - int colorXform; - Object globals, obj; - - if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) { - str = new ASCIIHexStream(str); - } else if (!strcmp(name, "ASCII85Decode") || !strcmp(name, "A85")) { - str = new ASCII85Stream(str); - } else if (!strcmp(name, "LZWDecode") || !strcmp(name, "LZW")) { - pred = 1; - columns = 1; - colors = 1; - bits = 8; - early = 1; - if (params->isDict()) { - params->dictLookup("Predictor", &obj, recursion); - if (obj.isInt()) - pred = obj.getInt(); - obj.free(); - params->dictLookup("Columns", &obj, recursion); - if (obj.isInt()) - columns = obj.getInt(); - obj.free(); - params->dictLookup("Colors", &obj, recursion); - if (obj.isInt()) - colors = obj.getInt(); - obj.free(); - params->dictLookup("BitsPerComponent", &obj, recursion); - if (obj.isInt()) - bits = obj.getInt(); - obj.free(); - params->dictLookup("EarlyChange", &obj, recursion); - if (obj.isInt()) - early = obj.getInt(); - obj.free(); - } - str = new LZWStream(str, pred, columns, colors, bits, early); - } else if (!strcmp(name, "RunLengthDecode") || !strcmp(name, "RL")) { - str = new RunLengthStream(str); - } else if (!strcmp(name, "CCITTFaxDecode") || !strcmp(name, "CCF")) { - encoding = 0; - endOfLine = gFalse; - byteAlign = gFalse; - columns = 1728; - rows = 0; - endOfBlock = gTrue; - black = gFalse; - if (params->isDict()) { - params->dictLookup("K", &obj, recursion); - if (obj.isInt()) { - encoding = obj.getInt(); - } - obj.free(); - params->dictLookup("EndOfLine", &obj, recursion); - if (obj.isBool()) { - endOfLine = obj.getBool(); - } - obj.free(); - params->dictLookup("EncodedByteAlign", &obj, recursion); - if (obj.isBool()) { - byteAlign = obj.getBool(); - } - obj.free(); - params->dictLookup("Columns", &obj, recursion); - if (obj.isInt()) { - columns = obj.getInt(); - } - obj.free(); - params->dictLookup("Rows", &obj, recursion); - if (obj.isInt()) { - rows = obj.getInt(); - } - obj.free(); - params->dictLookup("EndOfBlock", &obj, recursion); - if (obj.isBool()) { - endOfBlock = obj.getBool(); - } - obj.free(); - params->dictLookup("BlackIs1", &obj, recursion); - if (obj.isBool()) { - black = obj.getBool(); - } - obj.free(); - } - str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign, - columns, rows, endOfBlock, black); - } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) { - colorXform = -1; - if (params->isDict()) { - if (params->dictLookup("ColorTransform", &obj, recursion)->isInt()) { - colorXform = obj.getInt(); - } - obj.free(); - } - str = new DCTStream(str, colorXform); - } else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) { - pred = 1; - columns = 1; - colors = 1; - bits = 8; - if (params->isDict()) { - params->dictLookup("Predictor", &obj, recursion); - if (obj.isInt()) - pred = obj.getInt(); - obj.free(); - params->dictLookup("Columns", &obj, recursion); - if (obj.isInt()) - columns = obj.getInt(); - obj.free(); - params->dictLookup("Colors", &obj, recursion); - if (obj.isInt()) - colors = obj.getInt(); - obj.free(); - params->dictLookup("BitsPerComponent", &obj, recursion); - if (obj.isInt()) - bits = obj.getInt(); - obj.free(); - } - str = new FlateStream(str, pred, columns, colors, bits); - } else if (!strcmp(name, "JBIG2Decode")) { - if (params->isDict()) { - params->dictLookup("JBIG2Globals", &globals, recursion); - } - str = new JBIG2Stream(str, &globals); - globals.free(); - } else if (!strcmp(name, "JPXDecode")) { - str = new JPXStream(str); - } else { - error(errSyntaxError, getPos(), "Unknown filter '{0:s}'", name); - str = new EOFStream(str); - } - return str; -} - -//------------------------------------------------------------------------ -// BaseStream -//------------------------------------------------------------------------ - -BaseStream::BaseStream(Object *dictA) { - dict = *dictA; -} - -BaseStream::~BaseStream() { - dict.free(); -} - -//------------------------------------------------------------------------ -// FilterStream -//------------------------------------------------------------------------ - -FilterStream::FilterStream(Stream *strA) { - str = strA; -} - -FilterStream::~FilterStream() { -} - -void FilterStream::close() { - str->close(); -} - -void FilterStream::setPos(GFileOffset pos, int dir) { - error(errInternal, -1, "Called setPos() on FilterStream"); -} - -//------------------------------------------------------------------------ -// ImageStream -//------------------------------------------------------------------------ - -ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) { - int imgLineSize; - - str = strA; - width = widthA; - nComps = nCompsA; - nBits = nBitsA; - - nVals = width * nComps; - inputLineSize = (nVals * nBits + 7) >> 3; - if (width > INT_MAX / nComps || - nVals > (INT_MAX - 7) / nBits) { - // force a call to gmallocn(-1,...), which will throw an exception - inputLineSize = -1; - } - inputLine = (char *)gmallocn(inputLineSize, sizeof(char)); - if (nBits == 8) { - imgLine = (Guchar *)inputLine; - } else { - if (nBits == 1) { - imgLineSize = (nVals + 7) & ~7; - } else { - imgLineSize = nVals; - } - imgLine = (Guchar *)gmallocn(imgLineSize, sizeof(Guchar)); - } - imgIdx = nVals; -} - -ImageStream::~ImageStream() { - if (imgLine != (Guchar *)inputLine) { - gfree(imgLine); - } - gfree(inputLine); -} - -void ImageStream::reset() { - str->reset(); -} - -void ImageStream::close() { - str->close(); -} - -GBool ImageStream::getPixel(Guchar *pix) { - int i; - - if (imgIdx >= nVals) { - if (!getLine()) { - return gFalse; - } - imgIdx = 0; - } - for (i = 0; i < nComps; ++i) { - pix[i] = imgLine[imgIdx++]; - } - return gTrue; -} - -Guchar *ImageStream::getLine() { - Gulong buf, bitMask; - int bits; - int c; - int i; - char *p; - - if (str->getBlock(inputLine, inputLineSize) != inputLineSize) { - return NULL; - } - if (nBits == 1) { - p = inputLine; - for (i = 0; i < nVals; i += 8) { - c = *p++; - imgLine[i+0] = (Guchar)((c >> 7) & 1); - imgLine[i+1] = (Guchar)((c >> 6) & 1); - imgLine[i+2] = (Guchar)((c >> 5) & 1); - imgLine[i+3] = (Guchar)((c >> 4) & 1); - imgLine[i+4] = (Guchar)((c >> 3) & 1); - imgLine[i+5] = (Guchar)((c >> 2) & 1); - imgLine[i+6] = (Guchar)((c >> 1) & 1); - imgLine[i+7] = (Guchar)(c & 1); - } - } else if (nBits == 8) { - // special case: imgLine == inputLine - } else if (nBits == 16) { - for (i = 0; i < nVals; ++i) { - imgLine[i] = (Guchar)inputLine[2*i]; - } - } else { - bitMask = (1 << nBits) - 1; - buf = 0; - bits = 0; - p = inputLine; - for (i = 0; i < nVals; ++i) { - if (bits < nBits) { - buf = (buf << 8) | (*p++ & 0xff); - bits += 8; - } - imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask); - bits -= nBits; - } - } - return imgLine; -} - -void ImageStream::skipLine() { - str->getBlock(inputLine, inputLineSize); -} - - -//------------------------------------------------------------------------ -// StreamPredictor -//------------------------------------------------------------------------ - -StreamPredictor::StreamPredictor(Stream *strA, int predictorA, - int widthA, int nCompsA, int nBitsA) { - str = strA; - predictor = predictorA; - width = widthA; - nComps = nCompsA; - nBits = nBitsA; - predLine = NULL; - ok = gFalse; - - nVals = width * nComps; - pixBytes = (nComps * nBits + 7) >> 3; - rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes; - if (width <= 0 || nComps <= 0 || nBits <= 0 || - nComps > gfxColorMaxComps || - nBits > 16 || - width >= INT_MAX / nComps || // check for overflow in nVals - nVals >= (INT_MAX - 7) / nBits) { // check for overflow in rowBytes - return; - } - predLine = (Guchar *)gmalloc(rowBytes); - - reset(); - - ok = gTrue; -} - -StreamPredictor::~StreamPredictor() { - gfree(predLine); -} - -void StreamPredictor::reset() { - memset(predLine, 0, rowBytes); - predIdx = rowBytes; -} - -int StreamPredictor::lookChar() { - if (predIdx >= rowBytes) { - if (!getNextLine()) { - return EOF; - } - } - return predLine[predIdx]; -} - -int StreamPredictor::getChar() { - if (predIdx >= rowBytes) { - if (!getNextLine()) { - return EOF; - } - } - return predLine[predIdx++]; -} - -int StreamPredictor::getBlock(char *blk, int size) { - int n, m; - - n = 0; - while (n < size) { - if (predIdx >= rowBytes) { - if (!getNextLine()) { - break; - } - } - m = rowBytes - predIdx; - if (m > size - n) { - m = size - n; - } - memcpy(blk + n, predLine + predIdx, m); - predIdx += m; - n += m; - } - return n; -} - -GBool StreamPredictor::getNextLine() { - int curPred; - Guchar upLeftBuf[gfxColorMaxComps * 2 + 1]; - int left, up, upLeft, p, pa, pb, pc; - int c; - Gulong inBuf, outBuf, bitMask; - int inBits, outBits; - int i, j, k, kk; - - // get PNG optimum predictor number - if (predictor >= 10) { - if ((curPred = str->getRawChar()) == EOF) { - return gFalse; - } - curPred += 10; - } else { - curPred = predictor; - } - - // read the raw line, apply PNG (byte) predictor - memset(upLeftBuf, 0, pixBytes + 1); - for (i = pixBytes; i < rowBytes; ++i) { - for (j = pixBytes; j > 0; --j) { - upLeftBuf[j] = upLeftBuf[j-1]; - } - upLeftBuf[0] = predLine[i]; - if ((c = str->getRawChar()) == EOF) { - if (i > pixBytes) { - // this ought to return false, but some (broken) PDF files - // contain truncated image data, and Adobe apparently reads the - // last partial line - break; - } - return gFalse; - } - switch (curPred) { - case 11: // PNG sub - predLine[i] = (Guchar)(predLine[i - pixBytes] + c); - break; - case 12: // PNG up - predLine[i] = (Guchar)(predLine[i] + c); - break; - case 13: // PNG average - predLine[i] = (Guchar)(((predLine[i - pixBytes] + predLine[i]) >> 1) + c); - break; - case 14: // PNG Paeth - left = predLine[i - pixBytes]; - up = predLine[i]; - upLeft = upLeftBuf[pixBytes]; - p = left + up - upLeft; - if ((pa = p - left) < 0) - pa = -pa; - if ((pb = p - up) < 0) - pb = -pb; - if ((pc = p - upLeft) < 0) - pc = -pc; - if (pa <= pb && pa <= pc) - predLine[i] = (Guchar)(left + c); - else if (pb <= pc) - predLine[i] = (Guchar)(up + c); - else - predLine[i] = (Guchar)(upLeft + c); - break; - case 10: // PNG none - default: // no predictor or TIFF predictor - predLine[i] = (Guchar)c; - break; - } - } - - // apply TIFF (component) predictor - if (predictor == 2) { - if (nBits == 8) { - for (i = pixBytes; i < rowBytes; ++i) { - predLine[i] = (Guchar)(predLine[i] + predLine[i - nComps]); - } - } else if (nBits == 16) { - for (i = pixBytes; i < rowBytes; i += 2) { - c = ((predLine[i] + predLine[i - 2*nComps]) << 8) + - predLine[i + 1] + predLine[i + 1 - 2*nComps]; - predLine[i] = (Guchar)(c >> 8); - predLine[i+1] = (Guchar)(c & 0xff); - } - } else { - memset(upLeftBuf, 0, nComps); - bitMask = (1 << nBits) - 1; - inBuf = outBuf = 0; - inBits = outBits = 0; - j = k = pixBytes; - for (i = 0; i < width; ++i) { - for (kk = 0; kk < nComps; ++kk) { - if (inBits < nBits) { - inBuf = (inBuf << 8) | (predLine[j++] & 0xff); - inBits += 8; - } - upLeftBuf[kk] = (Guchar)((upLeftBuf[kk] + - (inBuf >> (inBits - nBits))) & bitMask); - inBits -= nBits; - outBuf = (outBuf << nBits) | upLeftBuf[kk]; - outBits += nBits; - if (outBits >= 8) { - predLine[k++] = (Guchar)(outBuf >> (outBits - 8)); - outBits -= 8; - } - } - } - if (outBits > 0) { - predLine[k++] = (Guchar)((outBuf << (8 - outBits)) + - (inBuf & ((1 << (8 - outBits)) - 1))); - } - } - } - - // reset to start of line - predIdx = pixBytes; - - return gTrue; -} - -//------------------------------------------------------------------------ -// SharedFile -//------------------------------------------------------------------------ - -class SharedFile { -public: - - SharedFile(FILE *fA); - SharedFile *copy(); - void free(); - int readBlock(char *buf, GFileOffset pos, int size); - GFileOffset getSize(); - -private: - - ~SharedFile(); - - FILE *f; - int refCnt; -#if MULTITHREADED - GMutex mutex; -#endif -}; - -SharedFile::SharedFile(FILE *fA) { - f = fA; - refCnt = 1; -#if MULTITHREADED - gInitMutex(&mutex); -#endif -} - -SharedFile::~SharedFile() { -#if MULTITHREADED - gDestroyMutex(&mutex); -#endif -} - -SharedFile *SharedFile::copy() { -#if MULTITHREADED - gLockMutex(&mutex); -#endif - ++refCnt; -#if MULTITHREADED - gUnlockMutex(&mutex); -#endif - return this; -} - -void SharedFile::free() { - int newCount; - -#if MULTITHREADED - gLockMutex(&mutex); -#endif - newCount = --refCnt; -#if MULTITHREADED - gUnlockMutex(&mutex); -#endif - if (newCount == 0) { - delete this; - } -} - -int SharedFile::readBlock(char *buf, GFileOffset pos, int size) { - int n; - -#if MULTITHREADED - gLockMutex(&mutex); -#endif - gfseek(f, pos, SEEK_SET); - n = (int)fread(buf, 1, size, f); -#if MULTITHREADED - gUnlockMutex(&mutex); -#endif - return n; -} - -GFileOffset SharedFile::getSize() { - GFileOffset size; - -#if MULTITHREADED - gLockMutex(&mutex); -#endif - gfseek(f, 0, SEEK_END); - size = gftell(f); -#if MULTITHREADED - gUnlockMutex(&mutex); -#endif - return size; -} - -//------------------------------------------------------------------------ -// FileStream -//------------------------------------------------------------------------ - -FileStream::FileStream(FILE *fA, GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA): - BaseStream(dictA) { - f = new SharedFile(fA); - start = startA; - limited = limitedA; - length = lengthA; - bufPtr = bufEnd = buf; - bufPos = start; -} - -FileStream::FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA): - BaseStream(dictA) { - f = fA->copy(); - start = startA; - limited = limitedA; - length = lengthA; - bufPtr = bufEnd = buf; - bufPos = start; -} - -FileStream::~FileStream() { - f->free(); -} - -Stream *FileStream::copy() { - Object dictA; - - dict.copy(&dictA); - return new FileStream(f, start, limited, length, &dictA); -} - -Stream *FileStream::makeSubStream(GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA) { - return new FileStream(f, startA, limitedA, lengthA, dictA); -} - -void FileStream::reset() { - bufPtr = bufEnd = buf; - bufPos = start; -} - -int FileStream::getBlock(char *blk, int size) { - int n, m; - - n = 0; - while (n < size) { - if (bufPtr >= bufEnd) { - if (!fillBuf()) { - break; - } - } - m = (int)(bufEnd - bufPtr); - if (m > size - n) { - m = size - n; - } - memcpy(blk + n, bufPtr, m); - bufPtr += m; - n += m; - } - return n; -} - -GBool FileStream::fillBuf() { - int n; - - bufPos += (int)(bufEnd - buf); - bufPtr = bufEnd = buf; - if (limited && bufPos >= start + length) { - return gFalse; - } - if (limited && bufPos + fileStreamBufSize > start + length) { - n = (int)(start + length - bufPos); - } else { - n = fileStreamBufSize; - } - n = f->readBlock(buf, bufPos, n); - bufEnd = buf + n; - if (bufPtr >= bufEnd) { - return gFalse; - } - return gTrue; -} - -void FileStream::setPos(GFileOffset pos, int dir) { - GFileOffset size; - - if (dir >= 0) { - bufPos = pos; - } else { - size = f->getSize(); - if (pos <= size) { - bufPos = size - pos; - } else { - bufPos = 0; - } - } - bufPtr = bufEnd = buf; -} - -void FileStream::moveStart(int delta) { - start += delta; - bufPtr = bufEnd = buf; - bufPos = start; -} - -//------------------------------------------------------------------------ -// MemStream -//------------------------------------------------------------------------ - -MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA): - BaseStream(dictA) { - buf = bufA; - start = startA; - length = lengthA; - bufEnd = buf + start + length; - bufPtr = buf + start; - needFree = gFalse; -} - -MemStream::~MemStream() { - if (needFree) { - gfree(buf); - } -} - -Stream *MemStream::copy() { - Object dictA; - - dict.copy(&dictA); - return new MemStream(buf, start, length, &dictA); -} - -Stream *MemStream::makeSubStream(GFileOffset startA, GBool limited, - GFileOffset lengthA, Object *dictA) { - MemStream *subStr; - Guint newStart, newLength; - - if (startA < start) { - newStart = start; - } else if (startA > start + length) { - newStart = start + (int)length; - } else { - newStart = (int)startA; - } - if (!limited || newStart + lengthA > start + length) { - newLength = start + length - newStart; - } else { - newLength = (Guint)lengthA; - } - subStr = new MemStream(buf, newStart, newLength, dictA); - return subStr; -} - -void MemStream::reset() { - bufPtr = buf + start; -} - -void MemStream::close() { -} - -int MemStream::getBlock(char *blk, int size) { - int n; - - if (size <= 0) { - return 0; - } - if (bufEnd - bufPtr < size) { - n = (int)(bufEnd - bufPtr); - } else { - n = size; - } - memcpy(blk, bufPtr, n); - bufPtr += n; - return n; -} - -void MemStream::setPos(GFileOffset pos, int dir) { - Guint i; - - if (dir >= 0) { - i = (Guint)pos; - } else { - i = (Guint)(start + length - pos); - } - if (i < start) { - i = start; - } else if (i > start + length) { - i = start + length; - } - bufPtr = buf + i; -} - -void MemStream::moveStart(int delta) { - start += delta; - length -= delta; - bufPtr = buf + start; -} - -//------------------------------------------------------------------------ -// EmbedStream -//------------------------------------------------------------------------ - -EmbedStream::EmbedStream(Stream *strA, Object *dictA, - GBool limitedA, GFileOffset lengthA): - BaseStream(dictA) { - str = strA; - limited = limitedA; - length = lengthA; -} - -EmbedStream::~EmbedStream() { -} - -Stream *EmbedStream::copy() { - Object dictA; - - dict.copy(&dictA); - return new EmbedStream(str, &dictA, limited, length); -} - -Stream *EmbedStream::makeSubStream(GFileOffset start, GBool limitedA, - GFileOffset lengthA, Object *dictA) { - error(errInternal, -1, "Called makeSubStream() on EmbedStream"); - return NULL; -} - -int EmbedStream::getChar() { - if (limited && !length) { - return EOF; - } - --length; - return str->getChar(); -} - -int EmbedStream::lookChar() { - if (limited && !length) { - return EOF; - } - return str->lookChar(); -} - -int EmbedStream::getBlock(char *blk, int size) { - if (size <= 0) { - return 0; - } - if (limited && length < (Guint)size) { - size = (int)length; - } - length -= size; - return str->getBlock(blk, size); -} - -void EmbedStream::setPos(GFileOffset pos, int dir) { - error(errInternal, -1, "Called setPos() on EmbedStream"); -} - -GFileOffset EmbedStream::getStart() { - error(errInternal, -1, "Called getStart() on EmbedStream"); - return 0; -} - -void EmbedStream::moveStart(int delta) { - error(errInternal, -1, "Called moveStart() on EmbedStream"); -} - -//------------------------------------------------------------------------ -// ASCIIHexStream -//------------------------------------------------------------------------ - -ASCIIHexStream::ASCIIHexStream(Stream *strA): - FilterStream(strA) { - buf = EOF; - eof = gFalse; -} - -ASCIIHexStream::~ASCIIHexStream() { - delete str; -} - -Stream *ASCIIHexStream::copy() { - return new ASCIIHexStream(str->copy()); -} - -void ASCIIHexStream::reset() { - str->reset(); - buf = EOF; - eof = gFalse; -} - -int ASCIIHexStream::lookChar() { - int c1, c2, x; - - if (buf != EOF) - return buf; - if (eof) { - buf = EOF; - return EOF; - } - do { - c1 = str->getChar(); - } while (isspace(c1)); - if (c1 == '>') { - eof = gTrue; - buf = EOF; - return buf; - } - do { - c2 = str->getChar(); - } while (isspace(c2)); - if (c2 == '>') { - eof = gTrue; - c2 = '0'; - } - if (c1 >= '0' && c1 <= '9') { - x = (c1 - '0') << 4; - } else if (c1 >= 'A' && c1 <= 'F') { - x = (c1 - 'A' + 10) << 4; - } else if (c1 >= 'a' && c1 <= 'f') { - x = (c1 - 'a' + 10) << 4; - } else if (c1 == EOF) { - eof = gTrue; - x = 0; - } else { - error(errSyntaxError, getPos(), - "Illegal character <{0:02x}> in ASCIIHex stream", c1); - x = 0; - } - if (c2 >= '0' && c2 <= '9') { - x += c2 - '0'; - } else if (c2 >= 'A' && c2 <= 'F') { - x += c2 - 'A' + 10; - } else if (c2 >= 'a' && c2 <= 'f') { - x += c2 - 'a' + 10; - } else if (c2 == EOF) { - eof = gTrue; - x = 0; - } else { - error(errSyntaxError, getPos(), - "Illegal character <{0:02x}> in ASCIIHex stream", c2); - } - buf = x & 0xff; - return buf; -} - -GString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 2) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("/ASCIIHexDecode filter\n"); - return s; -} - -GBool ASCIIHexStream::isBinary(GBool last) { - return str->isBinary(gFalse); -} - -//------------------------------------------------------------------------ -// ASCII85Stream -//------------------------------------------------------------------------ - -ASCII85Stream::ASCII85Stream(Stream *strA): - FilterStream(strA) { - index = n = 0; - eof = gFalse; -} - -ASCII85Stream::~ASCII85Stream() { - delete str; -} - -Stream *ASCII85Stream::copy() { - return new ASCII85Stream(str->copy()); -} - -void ASCII85Stream::reset() { - str->reset(); - index = n = 0; - eof = gFalse; -} - -int ASCII85Stream::lookChar() { - int k; - Gulong t; - - if (index >= n) { - if (eof) - return EOF; - index = 0; - do { - c[0] = str->getChar(); - } while (Lexer::isSpace(c[0])); - if (c[0] == '~' || c[0] == EOF) { - eof = gTrue; - n = 0; - return EOF; - } else if (c[0] == 'z') { - b[0] = b[1] = b[2] = b[3] = 0; - n = 4; - } else { - for (k = 1; k < 5; ++k) { - do { - c[k] = str->getChar(); - } while (Lexer::isSpace(c[k])); - if (c[k] == '~' || c[k] == EOF) - break; - } - n = k - 1; - if (k < 5 && (c[k] == '~' || c[k] == EOF)) { - for (++k; k < 5; ++k) - c[k] = 0x21 + 84; - eof = gTrue; - } - t = 0; - for (k = 0; k < 5; ++k) - t = t * 85 + (c[k] - 0x21); - for (k = 3; k >= 0; --k) { - b[k] = (int)(t & 0xff); - t >>= 8; - } - } - } - return b[index]; -} - -GString *ASCII85Stream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 2) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("/ASCII85Decode filter\n"); - return s; -} - -GBool ASCII85Stream::isBinary(GBool last) { - return str->isBinary(gFalse); -} - -//------------------------------------------------------------------------ -// LZWStream -//------------------------------------------------------------------------ - -LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors, - int bits, int earlyA): - FilterStream(strA) { - if (predictor != 1) { - pred = new StreamPredictor(this, predictor, columns, colors, bits); - if (!pred->isOk()) { - delete pred; - pred = NULL; - } - } else { - pred = NULL; - } - early = earlyA; - eof = gFalse; - inputBits = 0; - clearTable(); -} - -LZWStream::~LZWStream() { - if (pred) { - delete pred; - } - delete str; -} - -Stream *LZWStream::copy() { - if (pred) { - return new LZWStream(str->copy(), pred->getPredictor(), - pred->getWidth(), pred->getNComps(), - pred->getNBits(), early); - } else { - return new LZWStream(str->copy(), 1, 0, 0, 0, early); - } -} - -int LZWStream::getChar() { - if (pred) { - return pred->getChar(); - } - if (eof) { - return EOF; - } - if (seqIndex >= seqLength) { - if (!processNextCode()) { - return EOF; - } - } - return seqBuf[seqIndex++]; -} - -int LZWStream::lookChar() { - if (pred) { - return pred->lookChar(); - } - if (eof) { - return EOF; - } - if (seqIndex >= seqLength) { - if (!processNextCode()) { - return EOF; - } - } - return seqBuf[seqIndex]; -} - -int LZWStream::getRawChar() { - if (eof) { - return EOF; - } - if (seqIndex >= seqLength) { - if (!processNextCode()) { - return EOF; - } - } - return seqBuf[seqIndex++]; -} - -int LZWStream::getBlock(char *blk, int size) { - int n, m; - - if (pred) { - return pred->getBlock(blk, size); - } - if (eof) { - return 0; - } - n = 0; - while (n < size) { - if (seqIndex >= seqLength) { - if (!processNextCode()) { - break; - } - } - m = seqLength - seqIndex; - if (m > size - n) { - m = size - n; - } - memcpy(blk + n, seqBuf + seqIndex, m); - seqIndex += m; - n += m; - } - return n; -} - -void LZWStream::reset() { - str->reset(); - if (pred) { - pred->reset(); - } - eof = gFalse; - inputBits = 0; - clearTable(); -} - -GBool LZWStream::processNextCode() { - int code; - int nextLength; - int i, j; - - // check for EOF - if (eof) { - return gFalse; - } - - // check for eod and clear-table codes - start: - code = getCode(); - if (code == EOF || code == 257) { - eof = gTrue; - return gFalse; - } - if (code == 256) { - clearTable(); - goto start; - } - if (nextCode >= 4097) { - error(errSyntaxError, getPos(), - "Bad LZW stream - expected clear-table code"); - clearTable(); - } - - // process the next code - nextLength = seqLength + 1; - if (code < 256) { - seqBuf[0] = (Guchar)code; - seqLength = 1; - } else if (code < nextCode) { - seqLength = table[code].length; - for (i = seqLength - 1, j = code; i > 0; --i) { - seqBuf[i] = table[j].tail; - j = table[j].head; - } - seqBuf[0] = (Guchar)j; - } else if (code == nextCode) { - seqBuf[seqLength] = (Guchar)newChar; - ++seqLength; - } else { - error(errSyntaxError, getPos(), "Bad LZW stream - unexpected code"); - eof = gTrue; - return gFalse; - } - newChar = seqBuf[0]; - if (first) { - first = gFalse; - } else { - table[nextCode].length = nextLength; - table[nextCode].head = prevCode; - table[nextCode].tail = (Guchar)newChar; - ++nextCode; - if (nextCode + early == 512) - nextBits = 10; - else if (nextCode + early == 1024) - nextBits = 11; - else if (nextCode + early == 2048) - nextBits = 12; - } - prevCode = code; - - // reset buffer - seqIndex = 0; - - return gTrue; -} - -void LZWStream::clearTable() { - nextCode = 258; - nextBits = 9; - seqIndex = seqLength = 0; - first = gTrue; -} - -int LZWStream::getCode() { - int c; - int code; - - while (inputBits < nextBits) { - if ((c = str->getChar()) == EOF) - return EOF; - inputBuf = (inputBuf << 8) | (c & 0xff); - inputBits += 8; - } - code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1); - inputBits -= nextBits; - return code; -} - -GString *LZWStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 2 || pred) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("<< "); - if (!early) { - s->append("/EarlyChange 0 "); - } - s->append(">> /LZWDecode filter\n"); - return s; -} - -GBool LZWStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -//------------------------------------------------------------------------ -// RunLengthStream -//------------------------------------------------------------------------ - -RunLengthStream::RunLengthStream(Stream *strA): - FilterStream(strA) { - bufPtr = bufEnd = buf; - eof = gFalse; -} - -RunLengthStream::~RunLengthStream() { - delete str; -} - -Stream *RunLengthStream::copy() { - return new RunLengthStream(str->copy()); -} - -void RunLengthStream::reset() { - str->reset(); - bufPtr = bufEnd = buf; - eof = gFalse; -} - -int RunLengthStream::getBlock(char *blk, int size) { - int n, m; - - n = 0; - while (n < size) { - if (bufPtr >= bufEnd) { - if (!fillBuf()) { - break; - } - } - m = (int)(bufEnd - bufPtr); - if (m > size - n) { - m = size - n; - } - memcpy(blk + n, bufPtr, m); - bufPtr += m; - n += m; - } - return n; -} - -GString *RunLengthStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 2) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("/RunLengthDecode filter\n"); - return s; -} - -GBool RunLengthStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -GBool RunLengthStream::fillBuf() { - int c; - int n, i; - - if (eof) - return gFalse; - c = str->getChar(); - if (c == 0x80 || c == EOF) { - eof = gTrue; - return gFalse; - } - if (c < 0x80) { - n = c + 1; - for (i = 0; i < n; ++i) - buf[i] = (char)str->getChar(); - } else { - n = 0x101 - c; - c = str->getChar(); - for (i = 0; i < n; ++i) - buf[i] = (char)c; - } - bufPtr = buf; - bufEnd = buf + n; - return gTrue; -} - -//------------------------------------------------------------------------ -// CCITTFaxStream -//------------------------------------------------------------------------ - -CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, - GBool byteAlignA, int columnsA, int rowsA, - GBool endOfBlockA, GBool blackA): - FilterStream(strA) { - encoding = encodingA; - endOfLine = endOfLineA; - byteAlign = byteAlignA; - columns = columnsA; - if (columns < 1) { - columns = 1; - } else if (columns > INT_MAX - 3) { - columns = INT_MAX - 3; - } - rows = rowsA; - endOfBlock = endOfBlockA; - black = blackA; - blackXOR = black ? 0xff : 0x00; - // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = columns - // ---> max codingLine size = columns + 1 - // refLine has two extra guard entries at the end - // ---> max refLine size = columns + 3 - codingLine = (int *)gmallocn(columns + 1, sizeof(int)); - refLine = (int *)gmallocn(columns + 3, sizeof(int)); - - eof = gFalse; - row = 0; - nextLine2D = encoding < 0; - inputBits = 0; - codingLine[0] = columns; - nextCol = columns; - a0i = 0; - err = gFalse; - nErrors = 0; -} - -CCITTFaxStream::~CCITTFaxStream() { - delete str; - gfree(refLine); - gfree(codingLine); -} - -Stream *CCITTFaxStream::copy() { - return new CCITTFaxStream(str->copy(), encoding, endOfLine, - byteAlign, columns, rows, endOfBlock, black); -} - -void CCITTFaxStream::reset() { - int code1; - - str->reset(); - eof = gFalse; - row = 0; - nextLine2D = encoding < 0; - inputBits = 0; - codingLine[0] = columns; - nextCol = columns; - a0i = 0; - - // skip any initial zero bits and end-of-line marker, and get the 2D - // encoding tag - while ((code1 = lookBits(12)) == 0) { - eatBits(1); - } - if (code1 == 0x001) { - eatBits(12); - endOfLine = gTrue; - } - if (encoding > 0) { - nextLine2D = !lookBits(1); - eatBits(1); - } -} - -int CCITTFaxStream::getChar() { - int c, bitsNeeded, bitsAvail, bitsUsed; - - if (nextCol >= columns) { - if (eof) { - return EOF; - } - if (!readRow()) { - return EOF; - } - } - bitsAvail = codingLine[a0i] - nextCol; - if (bitsAvail > 8) { - c = (a0i & 1) ? 0x00 : 0xff; - } else { - c = 0; - bitsNeeded = 8; - do { - bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; - c <<= bitsUsed; - if (!(a0i & 1)) { - c |= 0xff >> (8 - bitsUsed); - } - bitsAvail -= bitsUsed; - bitsNeeded -= bitsUsed; - if (bitsAvail == 0) { - if (codingLine[a0i] >= columns) { - c <<= bitsNeeded; - break; - } - ++a0i; - bitsAvail = codingLine[a0i] - codingLine[a0i - 1]; - } - } while (bitsNeeded > 0); - } - nextCol += 8; - c ^= blackXOR; - return c; -} - -int CCITTFaxStream::lookChar() { - int c, bitsNeeded, bitsAvail, bitsUsed, i; - - if (nextCol >= columns) { - if (eof) { - return EOF; - } - if (!readRow()) { - return EOF; - } - } - bitsAvail = codingLine[a0i] - nextCol; - if (bitsAvail >= 8) { - c = (a0i & 1) ? 0x00 : 0xff; - } else { - i = a0i; - c = 0; - bitsNeeded = 8; - do { - bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; - c <<= bitsUsed; - if (!(i & 1)) { - c |= 0xff >> (8 - bitsUsed); - } - bitsAvail -= bitsUsed; - bitsNeeded -= bitsUsed; - if (bitsAvail == 0) { - if (codingLine[i] >= columns) { - c <<= bitsNeeded; - break; - } - ++i; - bitsAvail = codingLine[i] - codingLine[i - 1]; - } - } while (bitsNeeded > 0); - } - c ^= blackXOR; - return c; -} - -int CCITTFaxStream::getBlock(char *blk, int size) { - int bytesRead, bitsAvail, bitsNeeded, bitsUsed, byte, c; - - bytesRead = 0; - while (bytesRead < size) { - if (nextCol >= columns) { - if (eof) { - break; - } - if (!readRow()) { - break; - } - } - bitsAvail = codingLine[a0i] - nextCol; - byte = (a0i & 1) ? 0x00 : 0xff; - if (bitsAvail > 8) { - c = byte; - bitsAvail -= 8; - } else { - c = 0; - bitsNeeded = 8; - do { - bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; - c <<= bitsUsed; - c |= byte >> (8 - bitsUsed); - bitsAvail -= bitsUsed; - bitsNeeded -= bitsUsed; - if (bitsAvail == 0) { - if (codingLine[a0i] >= columns) { - c <<= bitsNeeded; - break; - } - ++a0i; - bitsAvail = codingLine[a0i] - codingLine[a0i - 1]; - byte ^= 0xff; - } - } while (bitsNeeded > 0); - } - nextCol += 8; - blk[bytesRead++] = (char)(c ^ blackXOR); - } - return bytesRead; -} - -inline void CCITTFaxStream::addPixels(int a1, int blackPixels) { - if (a1 > codingLine[a0i]) { - if (a1 > columns) { - error(errSyntaxError, getPos(), - "CCITTFax row is wrong length ({0:d})", a1); - err = gTrue; - ++nErrors; - a1 = columns; - } - if ((a0i & 1) ^ blackPixels) { - ++a0i; - } - codingLine[a0i] = a1; - } -} - -inline void CCITTFaxStream::addPixelsNeg(int a1, int blackPixels) { - if (a1 > codingLine[a0i]) { - if (a1 > columns) { - error(errSyntaxError, getPos(), - "CCITTFax row is wrong length ({0:d})", a1); - err = gTrue; - ++nErrors; - a1 = columns; - } - if ((a0i & 1) ^ blackPixels) { - ++a0i; - } - codingLine[a0i] = a1; - } else if (a1 < codingLine[a0i]) { - if (a1 < 0) { - error(errSyntaxError, getPos(), "Invalid CCITTFax code"); - err = gTrue; - ++nErrors; - a1 = 0; - } - while (a0i > 0 && a1 <= codingLine[a0i - 1]) { - --a0i; - } - codingLine[a0i] = a1; - } -} - -GBool CCITTFaxStream::readRow() { - int code1, code2, code3; - int b1i, blackPixels, i; - GBool gotEOL; - - // if at eof just return EOF - if (eof) { - return gFalse; - } - - err = gFalse; - - // 2-D encoding - if (nextLine2D) { - for (i = 0; codingLine[i] < columns; ++i) { - refLine[i] = codingLine[i]; - } - refLine[i++] = columns; - refLine[i++] = columns; - refLine[i] = columns; - codingLine[0] = 0; - a0i = 0; - b1i = 0; - blackPixels = 0; - // invariant: - // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] - // <= columns - // exception at left edge: - // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible - // exception at right edge: - // refLine[b1i] = refLine[b1i+1] = columns is possible - while (codingLine[a0i] < columns) { - code1 = getTwoDimCode(); - switch (code1) { - case twoDimPass: - addPixels(refLine[b1i + 1], blackPixels); - if (refLine[b1i + 1] < columns) { - b1i += 2; - } - break; - case twoDimHoriz: - code1 = code2 = 0; - if (blackPixels) { - do { - code1 += code3 = getBlackCode(); - } while (code3 >= 64); - do { - code2 += code3 = getWhiteCode(); - } while (code3 >= 64); - } else { - do { - code1 += code3 = getWhiteCode(); - } while (code3 >= 64); - do { - code2 += code3 = getBlackCode(); - } while (code3 >= 64); - } - addPixels(codingLine[a0i] + code1, blackPixels); - if (codingLine[a0i] < columns) { - addPixels(codingLine[a0i] + code2, blackPixels ^ 1); - } - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - break; - case twoDimVertR3: - addPixels(refLine[b1i] + 3, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - ++b1i; - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVertR2: - addPixels(refLine[b1i] + 2, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - ++b1i; - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVertR1: - addPixels(refLine[b1i] + 1, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - ++b1i; - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVert0: - addPixels(refLine[b1i], blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - ++b1i; - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVertL3: - addPixelsNeg(refLine[b1i] - 3, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - if (b1i > 0) { - --b1i; - } else { - ++b1i; - } - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVertL2: - addPixelsNeg(refLine[b1i] - 2, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - if (b1i > 0) { - --b1i; - } else { - ++b1i; - } - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case twoDimVertL1: - addPixelsNeg(refLine[b1i] - 1, blackPixels); - blackPixels ^= 1; - if (codingLine[a0i] < columns) { - if (b1i > 0) { - --b1i; - } else { - ++b1i; - } - while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { - b1i += 2; - } - } - break; - case EOF: - addPixels(columns, 0); - err = gTrue; - break; - default: - error(errSyntaxError, getPos(), - "Bad 2D code {0:04x} in CCITTFax stream", code1); - addPixels(columns, 0); - err = gTrue; - ++nErrors; - break; - } - } - - // 1-D encoding - } else { - codingLine[0] = 0; - a0i = 0; - blackPixels = 0; - while (codingLine[a0i] < columns) { - code1 = 0; - if (blackPixels) { - do { - code1 += code3 = getBlackCode(); - } while (code3 >= 64); - } else { - do { - code1 += code3 = getWhiteCode(); - } while (code3 >= 64); - } - addPixels(codingLine[a0i] + code1, blackPixels); - blackPixels ^= 1; - } - } - - // check for end-of-line marker, skipping over any extra zero bits - // (if EncodedByteAlign is true and EndOfLine is false, there can - // be "false" EOL markers -- i.e., if the last n unused bits in - // row i are set to zero, and the first 11-n bits in row i+1 - // happen to be zero -- so we don't look for EOL markers in this - // case) - gotEOL = gFalse; - if (!endOfBlock && row == rows - 1) { - eof = gTrue; - } else if (endOfLine || !byteAlign) { - code1 = lookBits(12); - if (endOfLine) { - while (code1 != EOF && code1 != 0x001) { - eatBits(1); - code1 = lookBits(12); - } - } else { - while (code1 == 0) { - eatBits(1); - code1 = lookBits(12); - } - } - if (code1 == 0x001) { - eatBits(12); - gotEOL = gTrue; - } - } - - // byte-align the row - // (Adobe apparently doesn't do byte alignment after EOL markers - // -- I've seen CCITT image data streams in two different formats, - // both with the byteAlign flag set: - // 1. xx:x0:01:yy:yy - // 2. xx:00:1y:yy:yy - // where xx is the previous line, yy is the next line, and colons - // separate bytes.) - if (byteAlign && !gotEOL) { - inputBits &= ~7; - } - - // check for end of stream - if (lookBits(1) == EOF) { - eof = gTrue; - } - - // get 2D encoding tag - if (!eof && encoding > 0) { - nextLine2D = !lookBits(1); - eatBits(1); - } - - // check for end-of-block marker - if (endOfBlock && !endOfLine && byteAlign) { - // in this case, we didn't check for an EOL code above, so we - // need to check here - code1 = lookBits(24); - if (code1 == 0x001001) { - eatBits(12); - gotEOL = gTrue; - } - } - if (endOfBlock && gotEOL) { - code1 = lookBits(12); - if (code1 == 0x001) { - eatBits(12); - if (encoding > 0) { - lookBits(1); - eatBits(1); - } - if (encoding >= 0) { - for (i = 0; i < 4; ++i) { - code1 = lookBits(12); - if (code1 != 0x001) { - error(errSyntaxError, getPos(), - "Bad RTC code in CCITTFax stream"); - ++nErrors; - } - eatBits(12); - if (encoding > 0) { - lookBits(1); - eatBits(1); - } - } - } - eof = gTrue; - } - - // look for an end-of-line marker after an error -- we only do - // this if we know the stream contains end-of-line markers because - // the "just plow on" technique tends to work better otherwise - } else if (err && endOfLine) { - while (1) { - code1 = lookBits(13); - if (code1 == EOF) { - eof = gTrue; - return gFalse; - } - if ((code1 >> 1) == 0x001) { - break; - } - eatBits(1); - } - eatBits(12); - if (encoding > 0) { - eatBits(1); - nextLine2D = !(code1 & 1); - } - } - - // corrupt CCITTFax streams can generate huge data expansion -- we - // avoid that case by aborting decode after 1000 errors - if (nErrors > 1000) { - error(errSyntaxError, getPos(), "Too many errors in CCITTFaxStream - aborting decode"); - eof = gTrue; - return gFalse; - } - - // set up for output - nextCol = 0; - a0i = (codingLine[0] > 0) ? 0 : 1; - - ++row; - - return gTrue; -} - -short CCITTFaxStream::getTwoDimCode() { - int code; - CCITTCode *p; - int n; - - code = 0; // make gcc happy - if (endOfBlock) { - if ((code = lookBits(7)) != EOF) { - p = &twoDimTab1[code]; - if (p->bits > 0) { - eatBits(p->bits); - return p->n; - } - } - } else { - for (n = 1; n <= 7; ++n) { - if ((code = lookBits(n)) == EOF) { - break; - } - if (n < 7) { - code <<= 7 - n; - } - p = &twoDimTab1[code]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - } - error(errSyntaxError, getPos(), - "Bad two dim code ({0:04x}) in CCITTFax stream", code); - ++nErrors; - return EOF; -} - -short CCITTFaxStream::getWhiteCode() { - short code; - CCITTCode *p; - int n; - - code = 0; // make gcc happy - if (endOfBlock) { - code = lookBits(12); - if (code == EOF) { - return 1; - } - if ((code >> 5) == 0) { - p = &whiteTab1[code]; - } else { - p = &whiteTab2[code >> 3]; - } - if (p->bits > 0) { - eatBits(p->bits); - return p->n; - } - } else { - for (n = 1; n <= 9; ++n) { - code = lookBits(n); - if (code == EOF) { - return 1; - } - if (n < 9) { - code = (short)(code << (9 - n)); - } - p = &whiteTab2[code]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - for (n = 11; n <= 12; ++n) { - code = lookBits(n); - if (code == EOF) { - return 1; - } - if (n < 12) { - code = (short)(code << (12 - n)); - } - p = &whiteTab1[code]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - } - error(errSyntaxError, getPos(), - "Bad white code ({0:04x}) in CCITTFax stream", code); - ++nErrors; - // eat a bit and return a positive number so that the caller doesn't - // go into an infinite loop - eatBits(1); - return 1; -} - -short CCITTFaxStream::getBlackCode() { - short code; - CCITTCode *p; - int n; - - code = 0; // make gcc happy - if (endOfBlock) { - code = lookBits(13); - if (code == EOF) { - return 1; - } - if ((code >> 7) == 0) { - p = &blackTab1[code]; - } else if ((code >> 9) == 0 && (code >> 7) != 0) { - p = &blackTab2[(code >> 1) - 64]; - } else { - p = &blackTab3[code >> 7]; - } - if (p->bits > 0) { - eatBits(p->bits); - return p->n; - } - } else { - for (n = 2; n <= 6; ++n) { - code = lookBits(n); - if (code == EOF) { - return 1; - } - if (n < 6) { - code = (short)(code << (6 - n)); - } - p = &blackTab3[code]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - for (n = 7; n <= 12; ++n) { - code = lookBits(n); - if (code == EOF) { - return 1; - } - if (n < 12) { - code = (short)(code << (12 - n)); - } - if (code >= 64) { - p = &blackTab2[code - 64]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - } - for (n = 10; n <= 13; ++n) { - code = lookBits(n); - if (code == EOF) { - return 1; - } - if (n < 13) { - code = (short)(code << (13 - n)); - } - p = &blackTab1[code]; - if (p->bits == n) { - eatBits(n); - return p->n; - } - } - } - error(errSyntaxError, getPos(), - "Bad black code ({0:04x}) in CCITTFax stream", code); - ++nErrors; - // eat a bit and return a positive number so that the caller doesn't - // go into an infinite loop - eatBits(1); - return 1; -} - -short CCITTFaxStream::lookBits(int n) { - int c; - - while (inputBits < n) { - if ((c = str->getChar()) == EOF) { - if (inputBits == 0) { - return EOF; - } - // near the end of the stream, the caller may ask for more bits - // than are available, but there may still be a valid code in - // however many bits are available -- we need to return correct - // data in this case - return (short)((inputBuf << (n - inputBits)) & (0xffffffff >> (32 - n))); - } - inputBuf = (inputBuf << 8) + c; - inputBits += 8; - } - return (short)((inputBuf >> (inputBits - n)) & (0xffffffff >> (32 - n))); -} - -GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - char s1[50]; - - if (psLevel < 2) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("<< "); - if (encoding != 0) { - sprintf(s1, "/K %d ", encoding); - s->append(s1); - } - if (endOfLine) { - s->append("/EndOfLine true "); - } - if (byteAlign) { - s->append("/EncodedByteAlign true "); - } - sprintf(s1, "/Columns %d ", columns); - s->append(s1); - if (rows != 0) { - sprintf(s1, "/Rows %d ", rows); - s->append(s1); - } - if (!endOfBlock) { - s->append("/EndOfBlock false "); - } - if (black) { - s->append("/BlackIs1 true "); - } - s->append(">> /CCITTFaxDecode filter\n"); - return s; -} - -GBool CCITTFaxStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -//------------------------------------------------------------------------ -// DCTStream -//------------------------------------------------------------------------ - -#if HAVE_JPEGLIB - -DCTStream::DCTStream(Stream *strA, GBool colorXformA): - FilterStream(strA) { - colorXform = colorXformA; - lineBuf = NULL; - inlineImage = str->isEmbedStream(); -} - -DCTStream::~DCTStream() { - delete str; -} - -Stream *DCTStream::copy() { - return new DCTStream(str->copy(), colorXform); -} - -void DCTStream::reset() { - int i; - - lineBuf = NULL; - error = gFalse; - - str->reset(); - - // initialize the libjpeg decompression object - decomp.err = jpeg_std_error(&errorMgr.err); - errorMgr.err.error_exit = &errorExit; - errorMgr.err.output_message = &errorMessage; - if (setjmp(errorMgr.setjmpBuf)) { - error = gTrue; - return; - } - jpeg_create_decompress(&decomp); - - // set up the data source manager - sourceMgr.src.next_input_byte = NULL; - sourceMgr.src.bytes_in_buffer = 0; - sourceMgr.src.init_source = &initSourceCbk; - sourceMgr.src.fill_input_buffer = &fillInputBufferCbk; - sourceMgr.src.skip_input_data = &skipInputDataCbk; - sourceMgr.src.resync_to_restart = &jpeg_resync_to_restart; - sourceMgr.src.term_source = &termSourceCbk; - sourceMgr.str = this; - decomp.src = &sourceMgr.src; - - // read the header - jpeg_read_header(&decomp, TRUE); - jpeg_calc_output_dimensions(&decomp); - - // set up the color transform - if (!decomp.saw_Adobe_marker && colorXform >= 0) { - if (decomp.num_components == 3) { - decomp.jpeg_color_space = colorXform ? JCS_YCbCr : JCS_RGB; - decomp.out_color_space = JCS_RGB; - decomp.out_color_components = 3; - } else if (decomp.num_components == 4) { - decomp.jpeg_color_space = colorXform ? JCS_YCCK : JCS_CMYK; - decomp.out_color_space = JCS_CMYK; - decomp.out_color_components = 4; - } - } - - // allocate a line buffer - if ((lineBufHeight = decomp.rec_outbuf_height) > 4) { - lineBufHeight = 4; - } - lineBuf = (char *)gmallocn(lineBufHeight * decomp.out_color_components, - decomp.output_width); - for (i = 0; i < lineBufHeight; ++i) { - lineBufRows[i] = lineBuf + - i * decomp.out_color_components * decomp.output_width; - } - bufPtr = bufEnd = lineBuf; - - // start up the decompression process - jpeg_start_decompress(&decomp); -} - -void DCTStream::close() { - // we don't call jpeg_finish_decompress() here because it will report - // an error if the full image wasn't read - if (setjmp(errorMgr.setjmpBuf)) { - goto skip; - } - jpeg_destroy_decompress(&decomp); - skip: - gfree(lineBuf); - FilterStream::close(); -} - -int DCTStream::getChar() { - if (error) { - return EOF; - } - if (bufPtr == bufEnd) { - if (!fillBuf()) { - return EOF; - } - } - return *bufPtr++ & 0xff; -} - -int DCTStream::lookChar() { - if (error) { - return EOF; - } - if (bufPtr == bufEnd) { - if (!fillBuf()) { - return EOF; - } - } - return *bufPtr & 0xff; -} - -int DCTStream::getBlock(char *blk, int size) { - int nRead, nAvail, n; - - if (error) { - return 0; - } - nRead = 0; - while (nRead < size) { - if (bufPtr == bufEnd) { - if (!fillBuf()) { - break; - } - } - nAvail = bufEnd - bufPtr; - n = (nAvail < size - nRead) ? nAvail : size - nRead; - memcpy(blk + nRead, bufPtr, n); - bufPtr += n; - nRead += n; - } - return nRead; -} - -GBool DCTStream::fillBuf() { - int nLines; - - if (setjmp(errorMgr.setjmpBuf)) { - error = gTrue; - return gFalse; - } - nLines = jpeg_read_scanlines(&decomp, (JSAMPARRAY)lineBufRows, - lineBufHeight); - bufPtr = lineBuf; - bufEnd = lineBuf + - nLines * decomp.out_color_components * decomp.output_width; - return nLines > 0; -} - -void DCTStream::errorExit(j_common_ptr d) { - DCTErrorMgr *errMgr = (DCTErrorMgr *)d->err; - longjmp(errMgr->setjmpBuf, 1); -} - -void DCTStream::errorMessage(j_common_ptr d) { -#if 0 // for debugging - char buf[JMSG_LENGTH_MAX]; - - (*d->err->format_message)(d, buf); - fprintf(stderr, "%s\n", buf); -#endif -} - -void DCTStream::initSourceCbk(j_decompress_ptr d) { - DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; - - sourceMgr->src.next_input_byte = NULL; - sourceMgr->src.bytes_in_buffer = 0; -} - -boolean DCTStream::fillInputBufferCbk(j_decompress_ptr d) { - DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; - int c, n; - - // for inline images, we need to read one byte at a time so we don't - // read past the end of the input data - if (sourceMgr->str->inlineImage) { - c = sourceMgr->str->str->getChar(); - if (c == EOF) { - sourceMgr->buf[0] = (char)0xff; - sourceMgr->buf[1] = (char)JPEG_EOI; - sourceMgr->src.bytes_in_buffer = 2; - } else { - sourceMgr->buf[0] = (char)c; - sourceMgr->src.bytes_in_buffer = 1; - } - } else { - n = sourceMgr->str->str->getBlock(sourceMgr->buf, dctStreamBufSize); - if (n > 0) { - sourceMgr->src.bytes_in_buffer = (size_t)n; - } else { - sourceMgr->buf[0] = (char)0xff; - sourceMgr->buf[1] = (char)JPEG_EOI; - sourceMgr->src.bytes_in_buffer = 2; - } - } - sourceMgr->src.next_input_byte = (JOCTET *)sourceMgr->buf; - return TRUE; -} - -void DCTStream::skipInputDataCbk(j_decompress_ptr d, long numBytes) { - DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; - - if (numBytes > 0) { - if ((long)sourceMgr->src.bytes_in_buffer < numBytes) { - sourceMgr->str->str->discardChars( - (Guint)(numBytes - sourceMgr->src.bytes_in_buffer)); - sourceMgr->src.bytes_in_buffer = 0; - } else { - sourceMgr->src.bytes_in_buffer -= numBytes; - sourceMgr->src.next_input_byte += numBytes; - } - } -} - -void DCTStream::termSourceCbk(j_decompress_ptr d) { -} - -#else // HAVE_JPEGLIB - -#define idctScaleA 1024 -#define idctScaleB 1138 -#define idctScaleC 1730 -#define idctScaleD 1609 -#define idctScaleE 1264 -#define idctScaleF 1922 -#define idctScaleG 1788 -#define idctScaleH 2923 -#define idctScaleI 2718 -#define idctScaleJ 2528 - -static int idctScaleMat[64] = { - idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB, - idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE, - idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF, - idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG, - idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB, - idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG, - idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF, - idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE -}; - -// color conversion parameters (16.16 fixed point format) -#define dctCrToR 91881 // 1.4020 -#define dctCbToG -22553 // -0.3441363 -#define dctCrToG -46802 // -0.71413636 -#define dctCbToB 116130 // 1.772 - -// The dctClip function clips signed integers to the [0,255] range. -// To handle valid DCT inputs, this must support an input range of at -// least [-256,511]. Invalid DCT inputs (e.g., from damaged PDF -// files) can result in arbitrary values, so we want to mask those -// out. We round the input range size up to a power of 2 (so we can -// use a bit mask), which gives us an input range of [-384,639]. The -// end result is: -// input output -// ---------- ------ -// <-384 X invalid inputs -> output is "don't care" -// -384..-257 0 invalid inputs, clipped -// -256..-1 0 valid inputs, need to be clipped -// 0..255 0..255 -// 256..511 255 valid inputs, need to be clipped -// 512..639 255 invalid inputs, clipped -// >=512 X invalid inputs -> output is "don't care" - -#define dctClipOffset 384 -#define dctClipMask 1023 -static Guchar dctClipData[1024]; - -static inline void dctClipInit() { - static int initDone = 0; - int i; - if (!initDone) { - for (i = -384; i < 0; ++i) { - dctClipData[dctClipOffset + i] = 0; - } - for (i = 0; i < 256; ++i) { - dctClipData[dctClipOffset + i] = (Guchar)i; - } - for (i = 256; i < 639; ++i) { - dctClipData[dctClipOffset + i] = 255; - } - initDone = 1; - } -} - -static inline Guchar dctClip(int x) { - return dctClipData[(dctClipOffset + x) & dctClipMask]; -} - -// zig zag decode map -static int dctZigZag[64] = { - 0, - 1, 8, - 16, 9, 2, - 3, 10, 17, 24, - 32, 25, 18, 11, 4, - 5, 12, 19, 26, 33, 40, - 48, 41, 34, 27, 20, 13, 6, - 7, 14, 21, 28, 35, 42, 49, 56, - 57, 50, 43, 36, 29, 22, 15, - 23, 30, 37, 44, 51, 58, - 59, 52, 45, 38, 31, - 39, 46, 53, 60, - 61, 54, 47, - 55, 62, - 63 -}; - -DCTStream::DCTStream(Stream *strA, GBool colorXformA): - FilterStream(strA) { - int i; - - colorXform = colorXformA; - progressive = interleaved = gFalse; - width = height = 0; - mcuWidth = mcuHeight = 0; - numComps = 0; - comp = 0; - x = y = 0; - for (i = 0; i < 4; ++i) { - frameBuf[i] = NULL; - } - rowBuf = NULL; - memset(dcHuffTables, 0, sizeof(dcHuffTables)); - memset(acHuffTables, 0, sizeof(acHuffTables)); - - dctClipInit(); -} - -DCTStream::~DCTStream() { - close(); - delete str; -} - -Stream *DCTStream::copy() { - return new DCTStream(str->copy(), colorXform); -} - -void DCTStream::reset() { - int i; - - str->reset(); - - progressive = interleaved = gFalse; - width = height = 0; - numComps = 0; - numQuantTables = 0; - numDCHuffTables = 0; - numACHuffTables = 0; - gotJFIFMarker = gFalse; - gotAdobeMarker = gFalse; - restartInterval = 0; - - if (!readHeader(gTrue)) { - // force an EOF condition - progressive = gTrue; - y = height; - return; - } - - // compute MCU size - if (numComps == 1) { - compInfo[0].hSample = compInfo[0].vSample = 1; - } - mcuWidth = compInfo[0].hSample; - mcuHeight = compInfo[0].vSample; - for (i = 1; i < numComps; ++i) { - if (compInfo[i].hSample > mcuWidth) { - mcuWidth = compInfo[i].hSample; - } - if (compInfo[i].vSample > mcuHeight) { - mcuHeight = compInfo[i].vSample; - } - } - mcuWidth *= 8; - mcuHeight *= 8; - - // figure out color transform - if (colorXform == -1) { - if (numComps == 3) { - if (gotJFIFMarker) { - colorXform = 1; - } else if (compInfo[0].id == 82 && compInfo[1].id == 71 && - compInfo[2].id == 66) { // ASCII "RGB" - colorXform = 0; - } else { - colorXform = 1; - } - } else { - colorXform = 0; - } - } - - if (progressive || !interleaved) { - - // allocate a buffer for the whole image - bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; - bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight; - if (bufWidth <= 0 || bufHeight <= 0 || - bufWidth > INT_MAX / bufWidth / (int)sizeof(int)) { - error(errSyntaxError, getPos(), "Invalid image size in DCT stream"); - y = height; - return; - } - for (i = 0; i < numComps; ++i) { - frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int)); - memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int)); - } - - // read the image data - do { - restartMarker = 0xd0; - restart(); - readScan(); - } while (readHeader(gFalse)); - - // decode - decodeImage(); - - // initialize counters - comp = 0; - x = 0; - y = 0; - - } else { - - if (scanInfo.numComps != numComps) { - error(errSyntaxError, getPos(), "Invalid scan in sequential DCT stream"); - y = height; - return; - } - - // allocate a buffer for one row of MCUs - bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; - rowBuf = (Guchar *)gmallocn(numComps * mcuHeight, bufWidth); - rowBufPtr = rowBufEnd = rowBuf; - - // initialize counters - y = -mcuHeight; - - restartMarker = 0xd0; - restart(); - } -} - -void DCTStream::close() { - int i; - - for (i = 0; i < 4; ++i) { - gfree(frameBuf[i]); - frameBuf[i] = NULL; - } - gfree(rowBuf); - rowBuf = NULL; - FilterStream::close(); -} - -int DCTStream::getChar() { - int c; - - if (progressive || !interleaved) { - if (y >= height) { - return EOF; - } - c = frameBuf[comp][y * bufWidth + x]; - if (++comp == numComps) { - comp = 0; - if (++x == width) { - x = 0; - ++y; - } - } - } else { - if (rowBufPtr == rowBufEnd) { - if (y + mcuHeight >= height) { - return EOF; - } - y += mcuHeight; - if (!readMCURow()) { - y = height; - return EOF; - } - } - c = *rowBufPtr++; - } - return c; -} - -int DCTStream::lookChar() { - if (progressive || !interleaved) { - if (y >= height) { - return EOF; - } - return frameBuf[comp][y * bufWidth + x]; - } else { - if (rowBufPtr == rowBufEnd) { - if (y + mcuHeight >= height) { - return EOF; - } - if (!readMCURow()) { - y = height; - return EOF; - } - } - return *rowBufPtr; - } -} - -int DCTStream::getBlock(char *blk, int size) { - int nRead, nAvail, n; - - if (progressive || !interleaved) { - if (y >= height) { - return 0; - } - for (nRead = 0; nRead < size; ++nRead) { - blk[nRead] = (char)frameBuf[comp][y * bufWidth + x]; - if (++comp == numComps) { - comp = 0; - if (++x == width) { - x = 0; - ++y; - if (y >= height) { - ++nRead; - break; - } - } - } - } - } else { - nRead = 0; - while (nRead < size) { - if (rowBufPtr == rowBufEnd) { - if (y + mcuHeight >= height) { - break; - } - y += mcuHeight; - if (!readMCURow()) { - y = height; - break; - } - } - nAvail = (int)(rowBufEnd - rowBufPtr); - n = (nAvail < size - nRead) ? nAvail : size - nRead; - memcpy(blk + nRead, rowBufPtr, n); - rowBufPtr += n; - nRead += n; - } - } - return nRead; -} - -void DCTStream::restart() { - int i; - - inputBits = 0; - restartCtr = restartInterval; - for (i = 0; i < numComps; ++i) { - compInfo[i].prevDC = 0; - } - eobRun = 0; -} - -// Read one row of MCUs from a sequential JPEG stream. -GBool DCTStream::readMCURow() { - int data1[64]; - Guchar data2[64]; - Guchar *p1, *p2; - int pY, pCb, pCr, pR, pG, pB; - int h, v, horiz, vert, hSub, vSub; - int x1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; - int c; - - for (cc = 0; cc < numComps; ++cc) { - if (scanInfo.dcHuffTable[cc] >= numDCHuffTables || - scanInfo.acHuffTable[cc] >= numACHuffTables) { - error(errSyntaxError, getPos(), - "Bad DCT data: invalid Huffman table index"); - return gFalse; - } - if (compInfo[cc].quantTable > numQuantTables) { - error(errSyntaxError, getPos(), - "Bad DCT data: invalid quant table index"); - return gFalse; - } - } - - for (x1 = 0; x1 < width; x1 += mcuWidth) { - - // deal with restart marker - if (restartInterval > 0 && restartCtr == 0) { - c = readMarker(); - if (c != restartMarker) { - error(errSyntaxError, getPos(), - "Bad DCT data: incorrect restart marker"); - return gFalse; - } - if (++restartMarker == 0xd8) - restartMarker = 0xd0; - restart(); - } - - // read one MCU - for (cc = 0; cc < numComps; ++cc) { - h = compInfo[cc].hSample; - v = compInfo[cc].vSample; - horiz = mcuWidth / h; - vert = mcuHeight / v; - hSub = horiz / 8; - vSub = vert / 8; - for (y2 = 0; y2 < mcuHeight; y2 += vert) { - for (x2 = 0; x2 < mcuWidth; x2 += horiz) { - if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], - &acHuffTables[scanInfo.acHuffTable[cc]], - &compInfo[cc].prevDC, - data1)) { - return gFalse; - } - transformDataUnit(quantTables[compInfo[cc].quantTable], - data1, data2); - if (hSub == 1 && vSub == 1 && x1+x2+8 <= width) { - for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { - p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc]; - p1[0] = data2[i]; - p1[ numComps] = data2[i+1]; - p1[2*numComps] = data2[i+2]; - p1[3*numComps] = data2[i+3]; - p1[4*numComps] = data2[i+4]; - p1[5*numComps] = data2[i+5]; - p1[6*numComps] = data2[i+6]; - p1[7*numComps] = data2[i+7]; - } - } else if (hSub == 2 && vSub == 2 && x1+x2+16 <= width) { - for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { - p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc]; - p2 = p1 + width * numComps; - p1[0] = p1[numComps] = - p2[0] = p2[numComps] = data2[i]; - p1[2*numComps] = p1[3*numComps] = - p2[2*numComps] = p2[3*numComps] = data2[i+1]; - p1[4*numComps] = p1[5*numComps] = - p2[4*numComps] = p2[5*numComps] = data2[i+2]; - p1[6*numComps] = p1[7*numComps] = - p2[6*numComps] = p2[7*numComps] = data2[i+3]; - p1[8*numComps] = p1[9*numComps] = - p2[8*numComps] = p2[9*numComps] = data2[i+4]; - p1[10*numComps] = p1[11*numComps] = - p2[10*numComps] = p2[11*numComps] = data2[i+5]; - p1[12*numComps] = p1[13*numComps] = - p2[12*numComps] = p2[13*numComps] = data2[i+6]; - p1[14*numComps] = p1[15*numComps] = - p2[14*numComps] = p2[15*numComps] = data2[i+7]; - } - } else { - p1 = &rowBuf[(y2 * width + (x1+x2)) * numComps + cc]; - i = 0; - for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { - for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { - for (y5 = 0; y5 < vSub; ++y5) { - for (x5 = 0; x5 < hSub && x1+x2+x4+x5 < width; ++x5) { - p1[((y4+y5) * width + (x4+x5)) * numComps] = data2[i]; - } - } - ++i; - } - } - } - } - } - } - --restartCtr; - } - - // color space conversion - if (colorXform) { - // convert YCbCr to RGB - if (numComps == 3) { - for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 3) { - pY = p1[0]; - pCb = p1[1] - 128; - pCr = p1[2] - 128; - pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; - p1[0] = dctClip(pR); - pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; - p1[1] = dctClip(pG); - pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; - p1[2] = dctClip(pB); - } - // convert YCbCrK to CMYK (K is passed through unchanged) - } else if (numComps == 4) { - for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 4) { - pY = p1[0]; - pCb = p1[1] - 128; - pCr = p1[2] - 128; - pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; - p1[0] = (Guchar)(255 - dctClip(pR)); - pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; - p1[1] = (Guchar)(255 - dctClip(pG)); - pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; - p1[2] = (Guchar)(255 - dctClip(pB)); - } - } - } - - rowBufPtr = rowBuf; - if (y + mcuHeight <= height) { - rowBufEnd = rowBuf + numComps * width * mcuHeight; - } else { - rowBufEnd = rowBuf + numComps * width * (height - y); - } - - return gTrue; -} - -// Read one scan from a progressive or non-interleaved JPEG stream. -void DCTStream::readScan() { - int data[64]; - int x1, y1, dx1, dy1, x2, y2, y3, cc, i; - int h, v, horiz, vert, vSub; - int *p1; - int c; - - for (cc = 0; cc < numComps; ++cc) { - if (scanInfo.comp[cc] && - (scanInfo.dcHuffTable[cc] >= numDCHuffTables || - ((!progressive || scanInfo.lastCoeff > 0) && - scanInfo.acHuffTable[cc] >= numACHuffTables))) { - error(errSyntaxError, getPos(), - "Bad DCT data: invalid Huffman table index"); - return; - } - if (compInfo[cc].quantTable > numQuantTables) { - error(errSyntaxError, getPos(), - "Bad DCT data: invalid quant table index"); - return; - } - } - - if (scanInfo.numComps == 1) { - for (cc = 0; cc < numComps; ++cc) { - if (scanInfo.comp[cc]) { - break; - } - } - dx1 = mcuWidth / compInfo[cc].hSample; - dy1 = mcuHeight / compInfo[cc].vSample; - } else { - dx1 = mcuWidth; - dy1 = mcuHeight; - } - - for (y1 = 0; y1 < height; y1 += dy1) { - for (x1 = 0; x1 < width; x1 += dx1) { - - // deal with restart marker - if (restartInterval > 0 && restartCtr == 0) { - c = readMarker(); - if (c != restartMarker) { - error(errSyntaxError, getPos(), - "Bad DCT data: incorrect restart marker"); - return; - } - if (++restartMarker == 0xd8) { - restartMarker = 0xd0; - } - restart(); - } - - // read one MCU - for (cc = 0; cc < numComps; ++cc) { - if (!scanInfo.comp[cc]) { - continue; - } - - h = compInfo[cc].hSample; - v = compInfo[cc].vSample; - horiz = mcuWidth / h; - vert = mcuHeight / v; - vSub = vert / 8; - for (y2 = 0; y2 < dy1; y2 += vert) { - for (x2 = 0; x2 < dx1; x2 += horiz) { - - // pull out the current values - p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; - for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { - data[i] = p1[0]; - data[i+1] = p1[1]; - data[i+2] = p1[2]; - data[i+3] = p1[3]; - data[i+4] = p1[4]; - data[i+5] = p1[5]; - data[i+6] = p1[6]; - data[i+7] = p1[7]; - p1 += bufWidth * vSub; - } - - // read one data unit - if (progressive) { - if (!readProgressiveDataUnit( - &dcHuffTables[scanInfo.dcHuffTable[cc]], - &acHuffTables[scanInfo.acHuffTable[cc]], - &compInfo[cc].prevDC, - data)) { - return; - } - } else { - if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], - &acHuffTables[scanInfo.acHuffTable[cc]], - &compInfo[cc].prevDC, - data)) { - return; - } - } - - // add the data unit into frameBuf - p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; - for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { - p1[0] = data[i]; - p1[1] = data[i+1]; - p1[2] = data[i+2]; - p1[3] = data[i+3]; - p1[4] = data[i+4]; - p1[5] = data[i+5]; - p1[6] = data[i+6]; - p1[7] = data[i+7]; - p1 += bufWidth * vSub; - } - } - } - } - --restartCtr; - } - } -} - -// Read one data unit from a sequential JPEG stream. -GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable, - DCTHuffTable *acHuffTable, - int *prevDC, int data[64]) { - int run, size, amp; - int c; - int i, j; - - if ((size = readHuffSym(dcHuffTable)) == 9999) { - return gFalse; - } - if (size > 0) { - if ((amp = readAmp(size)) == 9999) { - return gFalse; - } - } else { - amp = 0; - } - data[0] = *prevDC += amp; - for (i = 1; i < 64; ++i) { - data[i] = 0; - } - i = 1; - while (i < 64) { - run = 0; - while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) { - run += 0x10; - } - if (c == 9999) { - return gFalse; - } - if (c == 0x00) { - break; - } else { - run += (c >> 4) & 0x0f; - size = c & 0x0f; - amp = readAmp(size); - if (amp == 9999) { - return gFalse; - } - i += run; - if (i < 64) { - j = dctZigZag[i++]; - data[j] = amp; - } - } - } - return gTrue; -} - -// Read one data unit from a progressive JPEG stream. -GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable, - DCTHuffTable *acHuffTable, - int *prevDC, int data[64]) { - int run, size, amp, bit, c; - int i, j, k; - - // get the DC coefficient - i = scanInfo.firstCoeff; - if (i == 0) { - if (scanInfo.ah == 0) { - if ((size = readHuffSym(dcHuffTable)) == 9999) { - return gFalse; - } - if (size > 0) { - if ((amp = readAmp(size)) == 9999) { - return gFalse; - } - } else { - amp = 0; - } - data[0] += (*prevDC += amp) << scanInfo.al; - } else { - if ((bit = readBit()) == 9999) { - return gFalse; - } - if (bit) { - data[0] += 1 << scanInfo.al; - } - } - ++i; - } - if (scanInfo.lastCoeff == 0) { - return gTrue; - } - - // check for an EOB run - if (eobRun > 0) { - while (i <= scanInfo.lastCoeff) { - j = dctZigZag[i++]; - if (data[j] != 0) { - if ((bit = readBit()) == EOF) { - return gFalse; - } - if (bit) { - if (data[j] >= 0) { - data[j] += 1 << scanInfo.al; - } else { - data[j] -= 1 << scanInfo.al; - } - } - } - } - --eobRun; - return gTrue; - } - - // read the AC coefficients - while (i <= scanInfo.lastCoeff) { - if ((c = readHuffSym(acHuffTable)) == 9999) { - return gFalse; - } - - // ZRL - if (c == 0xf0) { - k = 0; - while (k < 16 && i <= scanInfo.lastCoeff) { - j = dctZigZag[i++]; - if (data[j] == 0) { - ++k; - } else { - if ((bit = readBit()) == EOF) { - return gFalse; - } - if (bit) { - if (data[j] >= 0) { - data[j] += 1 << scanInfo.al; - } else { - data[j] -= 1 << scanInfo.al; - } - } - } - } - - // EOB run - } else if ((c & 0x0f) == 0x00) { - j = c >> 4; - eobRun = 0; - for (k = 0; k < j; ++k) { - if ((bit = readBit()) == EOF) { - return gFalse; - } - eobRun = (eobRun << 1) | bit; - } - eobRun += 1 << j; - while (i <= scanInfo.lastCoeff) { - j = dctZigZag[i++]; - if (data[j] != 0) { - if ((bit = readBit()) == EOF) { - return gFalse; - } - if (bit) { - if (data[j] >= 0) { - data[j] += 1 << scanInfo.al; - } else { - data[j] -= 1 << scanInfo.al; - } - } - } - } - --eobRun; - break; - - // zero run and one AC coefficient - } else { - run = (c >> 4) & 0x0f; - size = c & 0x0f; - if ((amp = readAmp(size)) == 9999) { - return gFalse; - } - j = 0; // make gcc happy - for (k = 0; k <= run && i <= scanInfo.lastCoeff; ++k) { - j = dctZigZag[i++]; - while (data[j] != 0 && i <= scanInfo.lastCoeff) { - if ((bit = readBit()) == EOF) { - return gFalse; - } - if (bit) { - if (data[j] >= 0) { - data[j] += 1 << scanInfo.al; - } else { - data[j] -= 1 << scanInfo.al; - } - } - j = dctZigZag[i++]; - } - } - data[j] = amp << scanInfo.al; - } - } - - return gTrue; -} - -// Decode a progressive JPEG image. -void DCTStream::decodeImage() { - int dataIn[64]; - Guchar dataOut[64]; - Gushort *quantTable; - int pY, pCb, pCr, pR, pG, pB; - int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; - int h, v, horiz, vert, hSub, vSub; - int *p0, *p1, *p2; - - for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) { - for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) { - for (cc = 0; cc < numComps; ++cc) { - quantTable = quantTables[compInfo[cc].quantTable]; - h = compInfo[cc].hSample; - v = compInfo[cc].vSample; - horiz = mcuWidth / h; - vert = mcuHeight / v; - hSub = horiz / 8; - vSub = vert / 8; - for (y2 = 0; y2 < mcuHeight; y2 += vert) { - for (x2 = 0; x2 < mcuWidth; x2 += horiz) { - - // pull out the coded data unit - p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; - for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { - dataIn[i] = p1[0]; - dataIn[i+1] = p1[1]; - dataIn[i+2] = p1[2]; - dataIn[i+3] = p1[3]; - dataIn[i+4] = p1[4]; - dataIn[i+5] = p1[5]; - dataIn[i+6] = p1[6]; - dataIn[i+7] = p1[7]; - p1 += bufWidth * vSub; - } - - // transform - transformDataUnit(quantTable, dataIn, dataOut); - - // store back into frameBuf, doing replication for - // subsampled components - p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; - if (hSub == 1 && vSub == 1) { - for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { - p1[0] = dataOut[i] & 0xff; - p1[1] = dataOut[i+1] & 0xff; - p1[2] = dataOut[i+2] & 0xff; - p1[3] = dataOut[i+3] & 0xff; - p1[4] = dataOut[i+4] & 0xff; - p1[5] = dataOut[i+5] & 0xff; - p1[6] = dataOut[i+6] & 0xff; - p1[7] = dataOut[i+7] & 0xff; - p1 += bufWidth; - } - } else if (hSub == 2 && vSub == 2) { - p2 = p1 + bufWidth; - for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { - p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff; - p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i+1] & 0xff; - p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i+2] & 0xff; - p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i+3] & 0xff; - p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i+4] & 0xff; - p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i+5] & 0xff; - p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i+6] & 0xff; - p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i+7] & 0xff; - p1 += bufWidth * 2; - p2 += bufWidth * 2; - } - } else { - i = 0; - for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { - for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { - p2 = p1 + x4; - for (y5 = 0; y5 < vSub; ++y5) { - for (x5 = 0; x5 < hSub; ++x5) { - p2[x5] = dataOut[i] & 0xff; - } - p2 += bufWidth; - } - ++i; - } - p1 += bufWidth * vSub; - } - } - } - } - } - - // color space conversion - if (colorXform) { - // convert YCbCr to RGB - if (numComps == 3) { - for (y2 = 0; y2 < mcuHeight; ++y2) { - p0 = &frameBuf[0][(y1+y2) * bufWidth + x1]; - p1 = &frameBuf[1][(y1+y2) * bufWidth + x1]; - p2 = &frameBuf[2][(y1+y2) * bufWidth + x1]; - for (x2 = 0; x2 < mcuWidth; ++x2) { - pY = *p0; - pCb = *p1 - 128; - pCr = *p2 - 128; - pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; - *p0++ = dctClip(pR); - pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + - 32768) >> 16; - *p1++ = dctClip(pG); - pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; - *p2++ = dctClip(pB); - } - } - // convert YCbCrK to CMYK (K is passed through unchanged) - } else if (numComps == 4) { - for (y2 = 0; y2 < mcuHeight; ++y2) { - p0 = &frameBuf[0][(y1+y2) * bufWidth + x1]; - p1 = &frameBuf[1][(y1+y2) * bufWidth + x1]; - p2 = &frameBuf[2][(y1+y2) * bufWidth + x1]; - for (x2 = 0; x2 < mcuWidth; ++x2) { - pY = *p0; - pCb = *p1 - 128; - pCr = *p2 - 128; - pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; - *p0++ = 255 - dctClip(pR); - pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + - 32768) >> 16; - *p1++ = 255 - dctClip(pG); - pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; - *p2++ = 255 - dctClip(pB); - } - } - } - } - } - } -} - -// Transform one data unit -- this performs the dequantization and -// IDCT steps. This IDCT algorithm is taken from: -// Y. A. Reznik, A. T. Hinds, L. Yu, Z. Ni, and C-X. Zhang, -// "Efficient fixed-point approximations of the 8x8 inverse discrete -// cosine transform" (invited paper), Proc. SPIE Vol. 6696, Sep. 24, -// 2007. -// which is based on: -// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, -// "Practical Fast 1-D DCT Algorithms with 11 Multiplications", -// IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, -// 988-991. -// The stage numbers mentioned in the comments refer to Figure 1 in the -// Loeffler paper. -void DCTStream::transformDataUnit(Gushort *quantTable, - int dataIn[64], Guchar dataOut[64]) { - int v0, v1, v2, v3, v4, v5, v6, v7; - int t0, t1, t2, t3, t4, t5, t6, t7; - int *p, *scale; - Gushort *q; - int i; - - // dequant; inverse DCT on rows - for (i = 0; i < 64; i += 8) { - p = dataIn + i; - q = quantTable + i; - scale = idctScaleMat + i; - - // check for all-zero AC coefficients - if (p[1] == 0 && p[2] == 0 && p[3] == 0 && - p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) { - t0 = p[0] * q[0] * scale[0]; - if (i == 0) { - t0 += 1 << 12; // rounding bias - } - p[0] = t0; - p[1] = t0; - p[2] = t0; - p[3] = t0; - p[4] = t0; - p[5] = t0; - p[6] = t0; - p[7] = t0; - continue; - } - - // stage 4 - v0 = p[0] * q[0] * scale[0]; - if (i == 0) { - v0 += 1 << 12; // rounding bias - } - v1 = p[4] * q[4] * scale[4]; - v2 = p[2] * q[2] * scale[2]; - v3 = p[6] * q[6] * scale[6]; - t0 = p[1] * q[1] * scale[1]; - t1 = p[7] * q[7] * scale[7]; - v4 = t0 - t1; - v7 = t0 + t1; - v5 = p[3] * q[3] * scale[3]; - v6 = p[5] * q[5] * scale[5]; - - // stage 3 - t0 = v0 - v1; - v0 = v0 + v1; - v1 = t0; - t0 = v2 + (v2 >> 5); - t1 = t0 >> 2; - t2 = t1 + (v2 >> 4); // 41/128 * v2 - t3 = t0 - t1; // 99/128 * v2 - t4 = v3 + (v3 >> 5); - t5 = t4 >> 2; - t6 = t5 + (v3 >> 4); // 41/128 * v3 - t7 = t4 - t5; // 99/128 * v3 - v2 = t2 - t7; - v3 = t3 + t6; - t0 = v4 - v6; - v4 = v4 + v6; - v6 = t0; - t0 = v7 + v5; - v5 = v7 - v5; - v7 = t0; - - // stage 2 - t0 = v0 - v3; - v0 = v0 + v3; - v3 = t0; - t0 = v1 - v2; - v1 = v1 + v2; - v2 = t0; - t0 = (v4 >> 9) - v4; - t1 = v4 >> 1; // 1/2 * v4 - t2 = (t0 >> 2) - t0; // 1533/2048 * v4 - t3 = (v7 >> 9) - v7; - t4 = v7 >> 1; // 1/2 * v7 - t5 = (t3 >> 2) - t3; // 1533/2048 * v7 - v4 = t2 - t4; - v7 = t1 + t5; - t0 = (v5 >> 3) - (v5 >> 7); - t1 = t0 - (v5 >> 11); - t2 = t0 + (t1 >> 1); // 719/4096 * v5 - t3 = v5 - t0; // 113/256 * v5 - t4 = (v6 >> 3) - (v6 >> 7); - t5 = t4 - (v6 >> 11); - t6 = t4 + (t5 >> 1); // 719/4096 * v6 - t7 = v6 - t4; // 113/256 * v6 - v5 = t3 - t6; - v6 = t2 + t7; - - // stage 1 - p[0] = v0 + v7; - p[7] = v0 - v7; - p[1] = v1 + v6; - p[6] = v1 - v6; - p[2] = v2 + v5; - p[5] = v2 - v5; - p[3] = v3 + v4; - p[4] = v3 - v4; - } - - // inverse DCT on columns - for (i = 0; i < 8; ++i) { - p = dataIn + i; - - // check for all-zero AC coefficients - if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 && - p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) { - t0 = p[0*8]; - p[1*8] = t0; - p[2*8] = t0; - p[3*8] = t0; - p[4*8] = t0; - p[5*8] = t0; - p[6*8] = t0; - p[7*8] = t0; - continue; - } - - // stage 4 - v0 = p[0*8]; - v1 = p[4*8]; - v2 = p[2*8]; - v3 = p[6*8]; - t0 = p[1*8]; - t1 = p[7*8]; - v4 = t0 - t1; - v7 = t0 + t1; - v5 = p[3*8]; - v6 = p[5*8]; - - // stage 3 - t0 = v0 - v1; - v0 = v0 + v1; - v1 = t0; - t0 = v2 + (v2 >> 5); - t1 = t0 >> 2; - t2 = t1 + (v2 >> 4); // 41/128 * v2 - t3 = t0 - t1; // 99/128 * v2 - t4 = v3 + (v3 >> 5); - t5 = t4 >> 2; - t6 = t5 + (v3 >> 4); // 41/128 * v3 - t7 = t4 - t5; // 99/128 * v3 - v2 = t2 - t7; - v3 = t3 + t6; - t0 = v4 - v6; - v4 = v4 + v6; - v6 = t0; - t0 = v7 + v5; - v5 = v7 - v5; - v7 = t0; - - // stage 2 - t0 = v0 - v3; - v0 = v0 + v3; - v3 = t0; - t0 = v1 - v2; - v1 = v1 + v2; - v2 = t0; - t0 = (v4 >> 9) - v4; - t1 = v4 >> 1; // 1/2 * v4 - t2 = (t0 >> 2) - t0; // 1533/2048 * v4 - t3 = (v7 >> 9) - v7; - t4 = v7 >> 1; // 1/2 * v7 - t5 = (t3 >> 2) - t3; // 1533/2048 * v7 - v4 = t2 - t4; - v7 = t1 + t5; - t0 = (v5 >> 3) - (v5 >> 7); - t1 = t0 - (v5 >> 11); - t2 = t0 + (t1 >> 1); // 719/4096 * v5 - t3 = v5 - t0; // 113/256 * v5 - t4 = (v6 >> 3) - (v6 >> 7); - t5 = t4 - (v6 >> 11); - t6 = t4 + (t5 >> 1); // 719/4096 * v6 - t7 = v6 - t4; // 113/256 * v6 - v5 = t3 - t6; - v6 = t2 + t7; - - // stage 1 - p[0*8] = v0 + v7; - p[7*8] = v0 - v7; - p[1*8] = v1 + v6; - p[6*8] = v1 - v6; - p[2*8] = v2 + v5; - p[5*8] = v2 - v5; - p[3*8] = v3 + v4; - p[4*8] = v3 - v4; - } - - // convert to 8-bit integers - for (i = 0; i < 64; ++i) { - dataOut[i] = dctClip(128 + (dataIn[i] >> 13)); - } -} - -int DCTStream::readHuffSym(DCTHuffTable *table) { - Gushort code; - int bit; - int codeBits; - - code = 0; - codeBits = 0; - do { - // add a bit to the code - if ((bit = readBit()) == EOF) { - return 9999; - } - code = (Gushort)((code << 1) + bit); - ++codeBits; - - // look up code - if (code < table->firstCode[codeBits]) { - break; - } - if (code - table->firstCode[codeBits] < table->numCodes[codeBits]) { - code = (Gushort)(code - table->firstCode[codeBits]); - return table->sym[table->firstSym[codeBits] + code]; - } - } while (codeBits < 16); - - error(errSyntaxError, getPos(), "Bad Huffman code in DCT stream"); - return 9999; -} - -int DCTStream::readAmp(int size) { - int amp, bit; - int bits; - - amp = 0; - for (bits = 0; bits < size; ++bits) { - if ((bit = readBit()) == EOF) - return 9999; - amp = (amp << 1) + bit; - } - if (amp < (1 << (size - 1))) - amp -= (1 << size) - 1; - return amp; -} - -int DCTStream::readBit() { - int bit; - int c, c2; - - if (inputBits == 0) { - if ((c = str->getChar()) == EOF) - return EOF; - if (c == 0xff) { - do { - c2 = str->getChar(); - } while (c2 == 0xff); - if (c2 != 0x00) { - error(errSyntaxError, getPos(), "Bad DCT data: missing 00 after ff"); - return EOF; - } - } - inputBuf = c; - inputBits = 8; - } - bit = (inputBuf >> (inputBits - 1)) & 1; - --inputBits; - return bit; -} - -GBool DCTStream::readHeader(GBool frame) { - GBool doScan; - int n; - int c = 0; - - // read headers - doScan = gFalse; - while (!doScan) { - c = readMarker(); - switch (c) { - case 0xc0: // SOF0 (sequential) - case 0xc1: // SOF1 (extended sequential) - if (!frame) { - error(errSyntaxError, getPos(), - "Invalid DCT marker in scan <{0:02x}>", c); - return gFalse; - } - if (!readBaselineSOF()) { - return gFalse; - } - break; - case 0xc2: // SOF2 (progressive) - if (!frame) { - error(errSyntaxError, getPos(), - "Invalid DCT marker in scan <{0:02x}>", c); - return gFalse; - } - if (!readProgressiveSOF()) { - return gFalse; - } - break; - case 0xc4: // DHT - if (!readHuffmanTables()) { - return gFalse; - } - break; - case 0xd8: // SOI - if (!frame) { - error(errSyntaxError, getPos(), - "Invalid DCT marker in scan <{0:02x}>", c); - return gFalse; - } - break; - case 0xd9: // EOI - return gFalse; - case 0xda: // SOS - if (!readScanInfo()) { - return gFalse; - } - doScan = gTrue; - break; - case 0xdb: // DQT - if (!readQuantTables()) { - return gFalse; - } - break; - case 0xdd: // DRI - if (!readRestartInterval()) { - return gFalse; - } - break; - case 0xe0: // APP0 - if (!frame) { - error(errSyntaxError, getPos(), - "Invalid DCT marker in scan <{0:02x}>", c); - return gFalse; - } - if (!readJFIFMarker()) { - return gFalse; - } - break; - case 0xee: // APP14 - if (!frame) { - error(errSyntaxError, getPos(), - "Invalid DCT marker in scan <{0:02x}>", c); - return gFalse; - } - if (!readAdobeMarker()) { - return gFalse; - } - break; - case EOF: - error(errSyntaxError, getPos(), "Bad DCT header"); - return gFalse; - default: - // skip APPn / COM / etc. - if (c >= 0xe0) { - n = read16() - 2; - str->discardChars(n); - } else { - error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c); - return gFalse; - } - break; - } - } - - return gTrue; -} - -GBool DCTStream::readBaselineSOF() { - int prec; - int i; - int c; - - read16(); // length - prec = str->getChar(); - height = read16(); - width = read16(); - numComps = str->getChar(); - if (numComps <= 0 || numComps > 4) { - error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); - numComps = 0; - return gFalse; - } - if (prec != 8) { - error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); - return gFalse; - } - for (i = 0; i < numComps; ++i) { - compInfo[i].id = str->getChar(); - c = str->getChar(); - compInfo[i].hSample = (c >> 4) & 0x0f; - compInfo[i].vSample = c & 0x0f; - compInfo[i].quantTable = str->getChar(); - if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || - compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { - error(errSyntaxError, getPos(), "Bad DCT sampling factor"); - return gFalse; - } - if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { - error(errSyntaxError, getPos(), "Bad DCT quant table selector"); - return gFalse; - } - } - progressive = gFalse; - return gTrue; -} - -GBool DCTStream::readProgressiveSOF() { - int prec; - int i; - int c; - - read16(); // length - prec = str->getChar(); - height = read16(); - width = read16(); - numComps = str->getChar(); - if (numComps <= 0 || numComps > 4) { - error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); - numComps = 0; - return gFalse; - } - if (prec != 8) { - error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); - return gFalse; - } - for (i = 0; i < numComps; ++i) { - compInfo[i].id = str->getChar(); - c = str->getChar(); - compInfo[i].hSample = (c >> 4) & 0x0f; - compInfo[i].vSample = c & 0x0f; - compInfo[i].quantTable = str->getChar(); - if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || - compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { - error(errSyntaxError, getPos(), "Bad DCT sampling factor"); - return gFalse; - } - if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { - error(errSyntaxError, getPos(), "Bad DCT quant table selector"); - return gFalse; - } - } - progressive = gTrue; - return gTrue; -} - -GBool DCTStream::readScanInfo() { - int length; - int id, c; - int i, j; - - length = read16() - 2; - scanInfo.numComps = str->getChar(); - if (scanInfo.numComps <= 0 || scanInfo.numComps > 4) { - error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); - scanInfo.numComps = 0; - return gFalse; - } - --length; - if (length != 2 * scanInfo.numComps + 3) { - error(errSyntaxError, getPos(), "Bad DCT scan info block"); - return gFalse; - } - interleaved = scanInfo.numComps == numComps; - for (j = 0; j < numComps; ++j) { - scanInfo.comp[j] = gFalse; - } - for (i = 0; i < scanInfo.numComps; ++i) { - id = str->getChar(); - // some (broken) DCT streams reuse ID numbers, but at least they - // keep the components in order, so we check compInfo[i] first to - // work around the problem - if (id == compInfo[i].id) { - j = i; - } else { - for (j = 0; j < numComps; ++j) { - if (id == compInfo[j].id) { - break; - } - } - if (j == numComps) { - error(errSyntaxError, getPos(), - "Bad DCT component ID in scan info block"); - return gFalse; - } - } - if (scanInfo.comp[j]) { - error(errSyntaxError, getPos(), - "Invalid DCT component ID in scan info block"); - return gFalse; - } - scanInfo.comp[j] = gTrue; - c = str->getChar(); - scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f; - scanInfo.acHuffTable[j] = c & 0x0f; - } - scanInfo.firstCoeff = str->getChar(); - scanInfo.lastCoeff = str->getChar(); - if (scanInfo.firstCoeff < 0 || scanInfo.lastCoeff > 63 || - scanInfo.firstCoeff > scanInfo.lastCoeff) { - error(errSyntaxError, getPos(), - "Bad DCT coefficient numbers in scan info block"); - return gFalse; - } - c = str->getChar(); - scanInfo.ah = (c >> 4) & 0x0f; - scanInfo.al = c & 0x0f; - return gTrue; -} - -GBool DCTStream::readQuantTables() { - int length, prec, i, index; - - length = read16() - 2; - while (length > 0) { - index = str->getChar(); - prec = (index >> 4) & 0x0f; - index &= 0x0f; - if (prec > 1 || index >= 4) { - error(errSyntaxError, getPos(), "Bad DCT quantization table"); - return gFalse; - } - if (index == numQuantTables) { - numQuantTables = index + 1; - } - for (i = 0; i < 64; ++i) { - if (prec) { - quantTables[index][dctZigZag[i]] = (Gushort)read16(); - } else { - quantTables[index][dctZigZag[i]] = (Gushort)str->getChar(); - } - } - if (prec) { - length -= 129; - } else { - length -= 65; - } - } - return gTrue; -} - -GBool DCTStream::readHuffmanTables() { - DCTHuffTable *tbl; - int length; - int index; - Gushort code; - Guchar sym; - int i; - int c; - - length = read16() - 2; - while (length > 0) { - index = str->getChar(); - --length; - if ((index & 0x0f) >= 4) { - error(errSyntaxError, getPos(), "Bad DCT Huffman table"); - return gFalse; - } - if (index & 0x10) { - index &= 0x0f; - if (index >= numACHuffTables) - numACHuffTables = index+1; - tbl = &acHuffTables[index]; - } else { - index &= 0x0f; - if (index >= numDCHuffTables) - numDCHuffTables = index+1; - tbl = &dcHuffTables[index]; - } - sym = 0; - code = 0; - for (i = 1; i <= 16; ++i) { - c = str->getChar(); - tbl->firstSym[i] = sym; - tbl->firstCode[i] = code; - tbl->numCodes[i] = (Gushort)c; - sym = (Guchar)(sym + c); - code = (Gushort)((code + c) << 1); - } - length -= 16; - for (i = 0; i < sym; ++i) - tbl->sym[i] = (Guchar)str->getChar(); - length -= sym; - } - return gTrue; -} - -GBool DCTStream::readRestartInterval() { - int length; - - length = read16(); - if (length != 4) { - error(errSyntaxError, getPos(), "Bad DCT restart interval"); - return gFalse; - } - restartInterval = read16(); - return gTrue; -} - -GBool DCTStream::readJFIFMarker() { - int length, i; - char buf[5]; - int c; - - length = read16(); - length -= 2; - if (length >= 5) { - for (i = 0; i < 5; ++i) { - if ((c = str->getChar()) == EOF) { - error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); - return gFalse; - } - buf[i] = (char)c; - } - length -= 5; - if (!memcmp(buf, "JFIF\0", 5)) { - gotJFIFMarker = gTrue; - } - } - while (length > 0) { - if (str->getChar() == EOF) { - error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); - return gFalse; - } - --length; - } - return gTrue; -} - -GBool DCTStream::readAdobeMarker() { - int length, i; - char buf[12]; - int c; - - length = read16(); - if (length < 14) { - goto err; - } - for (i = 0; i < 12; ++i) { - if ((c = str->getChar()) == EOF) { - goto err; - } - buf[i] = (char)c; - } - if (!strncmp(buf, "Adobe", 5)) { - colorXform = buf[11]; - gotAdobeMarker = gTrue; - } - for (i = 14; i < length; ++i) { - if (str->getChar() == EOF) { - goto err; - } - } - return gTrue; - - err: - error(errSyntaxError, getPos(), "Bad DCT Adobe APP14 marker"); - return gFalse; -} - -GBool DCTStream::readTrailer() { - int c; - - c = readMarker(); - if (c != 0xd9) { // EOI - error(errSyntaxError, getPos(), "Bad DCT trailer"); - return gFalse; - } - return gTrue; -} - -int DCTStream::readMarker() { - int c; - - do { - do { - c = str->getChar(); - } while (c != 0xff && c != EOF); - do { - c = str->getChar(); - } while (c == 0xff); - } while (c == 0x00); - return c; -} - -int DCTStream::read16() { - int c1, c2; - - if ((c1 = str->getChar()) == EOF) - return EOF; - if ((c2 = str->getChar()) == EOF) - return EOF; - return (c1 << 8) + c2; -} - -#endif // HAVE_JPEGLIB - -GString *DCTStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 2) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("<< >> /DCTDecode filter\n"); - return s; -} - -GBool DCTStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -//------------------------------------------------------------------------ -// FlateStream -//------------------------------------------------------------------------ - -int FlateStream::codeLenCodeMap[flateMaxCodeLenCodes] = { - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 -}; - -FlateDecode FlateStream::lengthDecode[flateMaxLitCodes-257] = { - {0, 3}, - {0, 4}, - {0, 5}, - {0, 6}, - {0, 7}, - {0, 8}, - {0, 9}, - {0, 10}, - {1, 11}, - {1, 13}, - {1, 15}, - {1, 17}, - {2, 19}, - {2, 23}, - {2, 27}, - {2, 31}, - {3, 35}, - {3, 43}, - {3, 51}, - {3, 59}, - {4, 67}, - {4, 83}, - {4, 99}, - {4, 115}, - {5, 131}, - {5, 163}, - {5, 195}, - {5, 227}, - {0, 258}, - {0, 258}, - {0, 258} -}; - -FlateDecode FlateStream::distDecode[flateMaxDistCodes] = { - { 0, 1}, - { 0, 2}, - { 0, 3}, - { 0, 4}, - { 1, 5}, - { 1, 7}, - { 2, 9}, - { 2, 13}, - { 3, 17}, - { 3, 25}, - { 4, 33}, - { 4, 49}, - { 5, 65}, - { 5, 97}, - { 6, 129}, - { 6, 193}, - { 7, 257}, - { 7, 385}, - { 8, 513}, - { 8, 769}, - { 9, 1025}, - { 9, 1537}, - {10, 2049}, - {10, 3073}, - {11, 4097}, - {11, 6145}, - {12, 8193}, - {12, 12289}, - {13, 16385}, - {13, 24577} -}; - -static FlateCode flateFixedLitCodeTabCodes[512] = { - {7, 0x0100}, - {8, 0x0050}, - {8, 0x0010}, - {8, 0x0118}, - {7, 0x0110}, - {8, 0x0070}, - {8, 0x0030}, - {9, 0x00c0}, - {7, 0x0108}, - {8, 0x0060}, - {8, 0x0020}, - {9, 0x00a0}, - {8, 0x0000}, - {8, 0x0080}, - {8, 0x0040}, - {9, 0x00e0}, - {7, 0x0104}, - {8, 0x0058}, - {8, 0x0018}, - {9, 0x0090}, - {7, 0x0114}, - {8, 0x0078}, - {8, 0x0038}, - {9, 0x00d0}, - {7, 0x010c}, - {8, 0x0068}, - {8, 0x0028}, - {9, 0x00b0}, - {8, 0x0008}, - {8, 0x0088}, - {8, 0x0048}, - {9, 0x00f0}, - {7, 0x0102}, - {8, 0x0054}, - {8, 0x0014}, - {8, 0x011c}, - {7, 0x0112}, - {8, 0x0074}, - {8, 0x0034}, - {9, 0x00c8}, - {7, 0x010a}, - {8, 0x0064}, - {8, 0x0024}, - {9, 0x00a8}, - {8, 0x0004}, - {8, 0x0084}, - {8, 0x0044}, - {9, 0x00e8}, - {7, 0x0106}, - {8, 0x005c}, - {8, 0x001c}, - {9, 0x0098}, - {7, 0x0116}, - {8, 0x007c}, - {8, 0x003c}, - {9, 0x00d8}, - {7, 0x010e}, - {8, 0x006c}, - {8, 0x002c}, - {9, 0x00b8}, - {8, 0x000c}, - {8, 0x008c}, - {8, 0x004c}, - {9, 0x00f8}, - {7, 0x0101}, - {8, 0x0052}, - {8, 0x0012}, - {8, 0x011a}, - {7, 0x0111}, - {8, 0x0072}, - {8, 0x0032}, - {9, 0x00c4}, - {7, 0x0109}, - {8, 0x0062}, - {8, 0x0022}, - {9, 0x00a4}, - {8, 0x0002}, - {8, 0x0082}, - {8, 0x0042}, - {9, 0x00e4}, - {7, 0x0105}, - {8, 0x005a}, - {8, 0x001a}, - {9, 0x0094}, - {7, 0x0115}, - {8, 0x007a}, - {8, 0x003a}, - {9, 0x00d4}, - {7, 0x010d}, - {8, 0x006a}, - {8, 0x002a}, - {9, 0x00b4}, - {8, 0x000a}, - {8, 0x008a}, - {8, 0x004a}, - {9, 0x00f4}, - {7, 0x0103}, - {8, 0x0056}, - {8, 0x0016}, - {8, 0x011e}, - {7, 0x0113}, - {8, 0x0076}, - {8, 0x0036}, - {9, 0x00cc}, - {7, 0x010b}, - {8, 0x0066}, - {8, 0x0026}, - {9, 0x00ac}, - {8, 0x0006}, - {8, 0x0086}, - {8, 0x0046}, - {9, 0x00ec}, - {7, 0x0107}, - {8, 0x005e}, - {8, 0x001e}, - {9, 0x009c}, - {7, 0x0117}, - {8, 0x007e}, - {8, 0x003e}, - {9, 0x00dc}, - {7, 0x010f}, - {8, 0x006e}, - {8, 0x002e}, - {9, 0x00bc}, - {8, 0x000e}, - {8, 0x008e}, - {8, 0x004e}, - {9, 0x00fc}, - {7, 0x0100}, - {8, 0x0051}, - {8, 0x0011}, - {8, 0x0119}, - {7, 0x0110}, - {8, 0x0071}, - {8, 0x0031}, - {9, 0x00c2}, - {7, 0x0108}, - {8, 0x0061}, - {8, 0x0021}, - {9, 0x00a2}, - {8, 0x0001}, - {8, 0x0081}, - {8, 0x0041}, - {9, 0x00e2}, - {7, 0x0104}, - {8, 0x0059}, - {8, 0x0019}, - {9, 0x0092}, - {7, 0x0114}, - {8, 0x0079}, - {8, 0x0039}, - {9, 0x00d2}, - {7, 0x010c}, - {8, 0x0069}, - {8, 0x0029}, - {9, 0x00b2}, - {8, 0x0009}, - {8, 0x0089}, - {8, 0x0049}, - {9, 0x00f2}, - {7, 0x0102}, - {8, 0x0055}, - {8, 0x0015}, - {8, 0x011d}, - {7, 0x0112}, - {8, 0x0075}, - {8, 0x0035}, - {9, 0x00ca}, - {7, 0x010a}, - {8, 0x0065}, - {8, 0x0025}, - {9, 0x00aa}, - {8, 0x0005}, - {8, 0x0085}, - {8, 0x0045}, - {9, 0x00ea}, - {7, 0x0106}, - {8, 0x005d}, - {8, 0x001d}, - {9, 0x009a}, - {7, 0x0116}, - {8, 0x007d}, - {8, 0x003d}, - {9, 0x00da}, - {7, 0x010e}, - {8, 0x006d}, - {8, 0x002d}, - {9, 0x00ba}, - {8, 0x000d}, - {8, 0x008d}, - {8, 0x004d}, - {9, 0x00fa}, - {7, 0x0101}, - {8, 0x0053}, - {8, 0x0013}, - {8, 0x011b}, - {7, 0x0111}, - {8, 0x0073}, - {8, 0x0033}, - {9, 0x00c6}, - {7, 0x0109}, - {8, 0x0063}, - {8, 0x0023}, - {9, 0x00a6}, - {8, 0x0003}, - {8, 0x0083}, - {8, 0x0043}, - {9, 0x00e6}, - {7, 0x0105}, - {8, 0x005b}, - {8, 0x001b}, - {9, 0x0096}, - {7, 0x0115}, - {8, 0x007b}, - {8, 0x003b}, - {9, 0x00d6}, - {7, 0x010d}, - {8, 0x006b}, - {8, 0x002b}, - {9, 0x00b6}, - {8, 0x000b}, - {8, 0x008b}, - {8, 0x004b}, - {9, 0x00f6}, - {7, 0x0103}, - {8, 0x0057}, - {8, 0x0017}, - {8, 0x011f}, - {7, 0x0113}, - {8, 0x0077}, - {8, 0x0037}, - {9, 0x00ce}, - {7, 0x010b}, - {8, 0x0067}, - {8, 0x0027}, - {9, 0x00ae}, - {8, 0x0007}, - {8, 0x0087}, - {8, 0x0047}, - {9, 0x00ee}, - {7, 0x0107}, - {8, 0x005f}, - {8, 0x001f}, - {9, 0x009e}, - {7, 0x0117}, - {8, 0x007f}, - {8, 0x003f}, - {9, 0x00de}, - {7, 0x010f}, - {8, 0x006f}, - {8, 0x002f}, - {9, 0x00be}, - {8, 0x000f}, - {8, 0x008f}, - {8, 0x004f}, - {9, 0x00fe}, - {7, 0x0100}, - {8, 0x0050}, - {8, 0x0010}, - {8, 0x0118}, - {7, 0x0110}, - {8, 0x0070}, - {8, 0x0030}, - {9, 0x00c1}, - {7, 0x0108}, - {8, 0x0060}, - {8, 0x0020}, - {9, 0x00a1}, - {8, 0x0000}, - {8, 0x0080}, - {8, 0x0040}, - {9, 0x00e1}, - {7, 0x0104}, - {8, 0x0058}, - {8, 0x0018}, - {9, 0x0091}, - {7, 0x0114}, - {8, 0x0078}, - {8, 0x0038}, - {9, 0x00d1}, - {7, 0x010c}, - {8, 0x0068}, - {8, 0x0028}, - {9, 0x00b1}, - {8, 0x0008}, - {8, 0x0088}, - {8, 0x0048}, - {9, 0x00f1}, - {7, 0x0102}, - {8, 0x0054}, - {8, 0x0014}, - {8, 0x011c}, - {7, 0x0112}, - {8, 0x0074}, - {8, 0x0034}, - {9, 0x00c9}, - {7, 0x010a}, - {8, 0x0064}, - {8, 0x0024}, - {9, 0x00a9}, - {8, 0x0004}, - {8, 0x0084}, - {8, 0x0044}, - {9, 0x00e9}, - {7, 0x0106}, - {8, 0x005c}, - {8, 0x001c}, - {9, 0x0099}, - {7, 0x0116}, - {8, 0x007c}, - {8, 0x003c}, - {9, 0x00d9}, - {7, 0x010e}, - {8, 0x006c}, - {8, 0x002c}, - {9, 0x00b9}, - {8, 0x000c}, - {8, 0x008c}, - {8, 0x004c}, - {9, 0x00f9}, - {7, 0x0101}, - {8, 0x0052}, - {8, 0x0012}, - {8, 0x011a}, - {7, 0x0111}, - {8, 0x0072}, - {8, 0x0032}, - {9, 0x00c5}, - {7, 0x0109}, - {8, 0x0062}, - {8, 0x0022}, - {9, 0x00a5}, - {8, 0x0002}, - {8, 0x0082}, - {8, 0x0042}, - {9, 0x00e5}, - {7, 0x0105}, - {8, 0x005a}, - {8, 0x001a}, - {9, 0x0095}, - {7, 0x0115}, - {8, 0x007a}, - {8, 0x003a}, - {9, 0x00d5}, - {7, 0x010d}, - {8, 0x006a}, - {8, 0x002a}, - {9, 0x00b5}, - {8, 0x000a}, - {8, 0x008a}, - {8, 0x004a}, - {9, 0x00f5}, - {7, 0x0103}, - {8, 0x0056}, - {8, 0x0016}, - {8, 0x011e}, - {7, 0x0113}, - {8, 0x0076}, - {8, 0x0036}, - {9, 0x00cd}, - {7, 0x010b}, - {8, 0x0066}, - {8, 0x0026}, - {9, 0x00ad}, - {8, 0x0006}, - {8, 0x0086}, - {8, 0x0046}, - {9, 0x00ed}, - {7, 0x0107}, - {8, 0x005e}, - {8, 0x001e}, - {9, 0x009d}, - {7, 0x0117}, - {8, 0x007e}, - {8, 0x003e}, - {9, 0x00dd}, - {7, 0x010f}, - {8, 0x006e}, - {8, 0x002e}, - {9, 0x00bd}, - {8, 0x000e}, - {8, 0x008e}, - {8, 0x004e}, - {9, 0x00fd}, - {7, 0x0100}, - {8, 0x0051}, - {8, 0x0011}, - {8, 0x0119}, - {7, 0x0110}, - {8, 0x0071}, - {8, 0x0031}, - {9, 0x00c3}, - {7, 0x0108}, - {8, 0x0061}, - {8, 0x0021}, - {9, 0x00a3}, - {8, 0x0001}, - {8, 0x0081}, - {8, 0x0041}, - {9, 0x00e3}, - {7, 0x0104}, - {8, 0x0059}, - {8, 0x0019}, - {9, 0x0093}, - {7, 0x0114}, - {8, 0x0079}, - {8, 0x0039}, - {9, 0x00d3}, - {7, 0x010c}, - {8, 0x0069}, - {8, 0x0029}, - {9, 0x00b3}, - {8, 0x0009}, - {8, 0x0089}, - {8, 0x0049}, - {9, 0x00f3}, - {7, 0x0102}, - {8, 0x0055}, - {8, 0x0015}, - {8, 0x011d}, - {7, 0x0112}, - {8, 0x0075}, - {8, 0x0035}, - {9, 0x00cb}, - {7, 0x010a}, - {8, 0x0065}, - {8, 0x0025}, - {9, 0x00ab}, - {8, 0x0005}, - {8, 0x0085}, - {8, 0x0045}, - {9, 0x00eb}, - {7, 0x0106}, - {8, 0x005d}, - {8, 0x001d}, - {9, 0x009b}, - {7, 0x0116}, - {8, 0x007d}, - {8, 0x003d}, - {9, 0x00db}, - {7, 0x010e}, - {8, 0x006d}, - {8, 0x002d}, - {9, 0x00bb}, - {8, 0x000d}, - {8, 0x008d}, - {8, 0x004d}, - {9, 0x00fb}, - {7, 0x0101}, - {8, 0x0053}, - {8, 0x0013}, - {8, 0x011b}, - {7, 0x0111}, - {8, 0x0073}, - {8, 0x0033}, - {9, 0x00c7}, - {7, 0x0109}, - {8, 0x0063}, - {8, 0x0023}, - {9, 0x00a7}, - {8, 0x0003}, - {8, 0x0083}, - {8, 0x0043}, - {9, 0x00e7}, - {7, 0x0105}, - {8, 0x005b}, - {8, 0x001b}, - {9, 0x0097}, - {7, 0x0115}, - {8, 0x007b}, - {8, 0x003b}, - {9, 0x00d7}, - {7, 0x010d}, - {8, 0x006b}, - {8, 0x002b}, - {9, 0x00b7}, - {8, 0x000b}, - {8, 0x008b}, - {8, 0x004b}, - {9, 0x00f7}, - {7, 0x0103}, - {8, 0x0057}, - {8, 0x0017}, - {8, 0x011f}, - {7, 0x0113}, - {8, 0x0077}, - {8, 0x0037}, - {9, 0x00cf}, - {7, 0x010b}, - {8, 0x0067}, - {8, 0x0027}, - {9, 0x00af}, - {8, 0x0007}, - {8, 0x0087}, - {8, 0x0047}, - {9, 0x00ef}, - {7, 0x0107}, - {8, 0x005f}, - {8, 0x001f}, - {9, 0x009f}, - {7, 0x0117}, - {8, 0x007f}, - {8, 0x003f}, - {9, 0x00df}, - {7, 0x010f}, - {8, 0x006f}, - {8, 0x002f}, - {9, 0x00bf}, - {8, 0x000f}, - {8, 0x008f}, - {8, 0x004f}, - {9, 0x00ff} -}; - -FlateHuffmanTab FlateStream::fixedLitCodeTab = { - flateFixedLitCodeTabCodes, 9 -}; - -static FlateCode flateFixedDistCodeTabCodes[32] = { - {5, 0x0000}, - {5, 0x0010}, - {5, 0x0008}, - {5, 0x0018}, - {5, 0x0004}, - {5, 0x0014}, - {5, 0x000c}, - {5, 0x001c}, - {5, 0x0002}, - {5, 0x0012}, - {5, 0x000a}, - {5, 0x001a}, - {5, 0x0006}, - {5, 0x0016}, - {5, 0x000e}, - {0, 0x0000}, - {5, 0x0001}, - {5, 0x0011}, - {5, 0x0009}, - {5, 0x0019}, - {5, 0x0005}, - {5, 0x0015}, - {5, 0x000d}, - {5, 0x001d}, - {5, 0x0003}, - {5, 0x0013}, - {5, 0x000b}, - {5, 0x001b}, - {5, 0x0007}, - {5, 0x0017}, - {5, 0x000f}, - {0, 0x0000} -}; - -FlateHuffmanTab FlateStream::fixedDistCodeTab = { - flateFixedDistCodeTabCodes, 5 -}; - -FlateStream::FlateStream(Stream *strA, int predictor, int columns, - int colors, int bits): - FilterStream(strA) { - if (predictor != 1) { - pred = new StreamPredictor(this, predictor, columns, colors, bits); - if (!pred->isOk()) { - delete pred; - pred = NULL; - } - } else { - pred = NULL; - } - litCodeTab.codes = NULL; - distCodeTab.codes = NULL; - memset(buf, 0, flateWindow); -} - -FlateStream::~FlateStream() { - if (litCodeTab.codes != fixedLitCodeTab.codes) { - gfree(litCodeTab.codes); - } - if (distCodeTab.codes != fixedDistCodeTab.codes) { - gfree(distCodeTab.codes); - } - if (pred) { - delete pred; - } - delete str; -} - -Stream *FlateStream::copy() { - if (pred) { - return new FlateStream(str->copy(), pred->getPredictor(), - pred->getWidth(), pred->getNComps(), - pred->getNBits()); - } else { - return new FlateStream(str->copy(), 1, 0, 0, 0); - } -} - -void FlateStream::reset() { - int cmf, flg; - - index = 0; - remain = 0; - codeBuf = 0; - codeSize = 0; - compressedBlock = gFalse; - endOfBlock = gTrue; - eof = gTrue; - - str->reset(); - if (pred) { - pred->reset(); - } - - // read header - //~ need to look at window size? - endOfBlock = eof = gTrue; - cmf = str->getChar(); - flg = str->getChar(); - if (cmf == EOF || flg == EOF) - return; - if ((cmf & 0x0f) != 0x08) { - error(errSyntaxError, getPos(), - "Unknown compression method in flate stream"); - return; - } - if ((((cmf << 8) + flg) % 31) != 0) { - error(errSyntaxError, getPos(), "Bad FCHECK in flate stream"); - return; - } - if (flg & 0x20) { - error(errSyntaxError, getPos(), "FDICT bit set in flate stream"); - return; - } - - eof = gFalse; -} - -int FlateStream::getChar() { - int c; - - if (pred) { - return pred->getChar(); - } - while (remain == 0) { - if (endOfBlock && eof) - return EOF; - readSome(); - } - c = buf[index]; - index = (index + 1) & flateMask; - --remain; - return c; -} - -int FlateStream::lookChar() { - int c; - - if (pred) { - return pred->lookChar(); - } - while (remain == 0) { - if (endOfBlock && eof) - return EOF; - readSome(); - } - c = buf[index]; - return c; -} - -int FlateStream::getRawChar() { - int c; - - while (remain == 0) { - if (endOfBlock && eof) - return EOF; - readSome(); - } - c = buf[index]; - index = (index + 1) & flateMask; - --remain; - return c; -} - -int FlateStream::getBlock(char *blk, int size) { - int n; - - if (pred) { - return pred->getBlock(blk, size); - } - - n = 0; - while (n < size) { - if (remain == 0) { - if (endOfBlock && eof) { - break; - } - readSome(); - } - while (remain && n < size) { - blk[n++] = buf[index]; - index = (index + 1) & flateMask; - --remain; - } - } - return n; -} - -GString *FlateStream::getPSFilter(int psLevel, const char *indent) { - GString *s; - - if (psLevel < 3 || pred) { - return NULL; - } - if (!(s = str->getPSFilter(psLevel, indent))) { - return NULL; - } - s->append(indent)->append("<< >> /FlateDecode filter\n"); - return s; -} - -GBool FlateStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -void FlateStream::readSome() { - int code1, code2; - int len, dist; - int i, j, k; - int c; - - if (endOfBlock) { - if (!startBlock()) - return; - } - - if (compressedBlock) { - if ((code1 = getHuffmanCodeWord(&litCodeTab)) == EOF) - goto err; - if (code1 < 256) { - buf[index] = (Guchar)code1; - remain = 1; - } else if (code1 == 256) { - endOfBlock = gTrue; - remain = 0; - } else { - code1 -= 257; - code2 = lengthDecode[code1].bits; - if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) - goto err; - len = lengthDecode[code1].first + code2; - if ((code1 = getHuffmanCodeWord(&distCodeTab)) == EOF) - goto err; - code2 = distDecode[code1].bits; - if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) - goto err; - dist = distDecode[code1].first + code2; - i = index; - j = (index - dist) & flateMask; - for (k = 0; k < len; ++k) { - buf[i] = buf[j]; - i = (i + 1) & flateMask; - j = (j + 1) & flateMask; - } - remain = len; - } - - } else { - len = (blockLen < flateWindow) ? blockLen : flateWindow; - for (i = 0, j = index; i < len; ++i, j = (j + 1) & flateMask) { - if ((c = str->getChar()) == EOF) { - endOfBlock = eof = gTrue; - break; - } - buf[j] = (Guchar)c; - } - remain = i; - blockLen -= len; - if (blockLen == 0) - endOfBlock = gTrue; - } - - return; - -err: - error(errSyntaxError, getPos(), "Unexpected end of file in flate stream"); - endOfBlock = eof = gTrue; - remain = 0; -} - -GBool FlateStream::startBlock() { - int blockHdr; - int c; - int check; - - // free the code tables from the previous block - if (litCodeTab.codes != fixedLitCodeTab.codes) { - gfree(litCodeTab.codes); - } - litCodeTab.codes = NULL; - if (distCodeTab.codes != fixedDistCodeTab.codes) { - gfree(distCodeTab.codes); - } - distCodeTab.codes = NULL; - - // read block header - blockHdr = getCodeWord(3); - if (blockHdr & 1) - eof = gTrue; - blockHdr >>= 1; - - // uncompressed block - if (blockHdr == 0) { - compressedBlock = gFalse; - if ((c = str->getChar()) == EOF) - goto err; - blockLen = c & 0xff; - if ((c = str->getChar()) == EOF) - goto err; - blockLen |= (c & 0xff) << 8; - if ((c = str->getChar()) == EOF) - goto err; - check = c & 0xff; - if ((c = str->getChar()) == EOF) - goto err; - check |= (c & 0xff) << 8; - if (check != (~blockLen & 0xffff)) - error(errSyntaxError, getPos(), - "Bad uncompressed block length in flate stream"); - codeBuf = 0; - codeSize = 0; - - // compressed block with fixed codes - } else if (blockHdr == 1) { - compressedBlock = gTrue; - loadFixedCodes(); - - // compressed block with dynamic codes - } else if (blockHdr == 2) { - compressedBlock = gTrue; - if (!readDynamicCodes()) { - goto err; - } - - // unknown block type - } else { - goto err; - } - - endOfBlock = gFalse; - return gTrue; - -err: - error(errSyntaxError, getPos(), "Bad block header in flate stream"); - endOfBlock = eof = gTrue; - return gFalse; -} - -void FlateStream::loadFixedCodes() { - litCodeTab.codes = fixedLitCodeTab.codes; - litCodeTab.maxLen = fixedLitCodeTab.maxLen; - distCodeTab.codes = fixedDistCodeTab.codes; - distCodeTab.maxLen = fixedDistCodeTab.maxLen; -} - -GBool FlateStream::readDynamicCodes() { - int numCodeLenCodes; - int numLitCodes; - int numDistCodes; - int codeLenCodeLengths[flateMaxCodeLenCodes]; - FlateHuffmanTab codeLenCodeTab; - int len, repeat, code; - int i; - - codeLenCodeTab.codes = NULL; - - // read lengths - if ((numLitCodes = getCodeWord(5)) == EOF) { - goto err; - } - numLitCodes += 257; - if ((numDistCodes = getCodeWord(5)) == EOF) { - goto err; - } - numDistCodes += 1; - if ((numCodeLenCodes = getCodeWord(4)) == EOF) { - goto err; - } - numCodeLenCodes += 4; - if (numLitCodes > flateMaxLitCodes || - numDistCodes > flateMaxDistCodes || - numCodeLenCodes > flateMaxCodeLenCodes) { - goto err; - } - - // build the code length code table - for (i = 0; i < flateMaxCodeLenCodes; ++i) { - codeLenCodeLengths[i] = 0; - } - for (i = 0; i < numCodeLenCodes; ++i) { - if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) { - goto err; - } - } - compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab); - - // build the literal and distance code tables - len = 0; - repeat = 0; - i = 0; - while (i < numLitCodes + numDistCodes) { - if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) { - goto err; - } - if (code == 16) { - if ((repeat = getCodeWord(2)) == EOF) { - goto err; - } - repeat += 3; - if (i + repeat > numLitCodes + numDistCodes) { - goto err; - } - for (; repeat > 0; --repeat) { - codeLengths[i++] = len; - } - } else if (code == 17) { - if ((repeat = getCodeWord(3)) == EOF) { - goto err; - } - repeat += 3; - if (i + repeat > numLitCodes + numDistCodes) { - goto err; - } - len = 0; - for (; repeat > 0; --repeat) { - codeLengths[i++] = 0; - } - } else if (code == 18) { - if ((repeat = getCodeWord(7)) == EOF) { - goto err; - } - repeat += 11; - if (i + repeat > numLitCodes + numDistCodes) { - goto err; - } - len = 0; - for (; repeat > 0; --repeat) { - codeLengths[i++] = 0; - } - } else { - codeLengths[i++] = len = code; - } - } - compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab); - compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab); - - gfree(codeLenCodeTab.codes); - return gTrue; - -err: - error(errSyntaxError, getPos(), "Bad dynamic code table in flate stream"); - gfree(codeLenCodeTab.codes); - return gFalse; -} - -// Convert an array of lengths, in value order, into a -// Huffman code lookup table. -void FlateStream::compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab) { - int tabSize, len, code, code2, skip, val, i, t; - - // find max code length - tab->maxLen = 0; - for (val = 0; val < n; ++val) { - if (lengths[val] > tab->maxLen) { - tab->maxLen = lengths[val]; - } - } - - // allocate the table - tabSize = 1 << tab->maxLen; - tab->codes = (FlateCode *)gmallocn(tabSize, sizeof(FlateCode)); - - // clear the table - for (i = 0; i < tabSize; ++i) { - tab->codes[i].len = 0; - tab->codes[i].val = 0; - } - - // build the table - for (len = 1, code = 0, skip = 2; - len <= tab->maxLen; - ++len, code <<= 1, skip <<= 1) { - for (val = 0; val < n; ++val) { - if (lengths[val] == len) { - - // bit-reverse the code - code2 = 0; - t = code; - for (i = 0; i < len; ++i) { - code2 = (code2 << 1) | (t & 1); - t >>= 1; - } - - // fill in the table entries - for (i = code2; i < tabSize; i += skip) { - tab->codes[i].len = (Gushort)len; - tab->codes[i].val = (Gushort)val; - } - - ++code; - } - } - } -} - -int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) { - FlateCode *code; - int c; - - while (codeSize < tab->maxLen) { - if ((c = str->getChar()) == EOF) { - break; - } - codeBuf |= (c & 0xff) << codeSize; - codeSize += 8; - } - code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)]; - if (codeSize == 0 || codeSize < code->len || code->len == 0) { - return EOF; - } - codeBuf >>= code->len; - codeSize -= code->len; - return (int)code->val; -} - -int FlateStream::getCodeWord(int bits) { - int c; - - while (codeSize < bits) { - if ((c = str->getChar()) == EOF) - return EOF; - codeBuf |= (c & 0xff) << codeSize; - codeSize += 8; - } - c = codeBuf & ((1 << bits) - 1); - codeBuf >>= bits; - codeSize -= bits; - return c; -} - -//------------------------------------------------------------------------ -// EOFStream -//------------------------------------------------------------------------ - -EOFStream::EOFStream(Stream *strA): - FilterStream(strA) { -} - -EOFStream::~EOFStream() { - delete str; -} - -Stream *EOFStream::copy() { - return new EOFStream(str->copy()); -} - -//------------------------------------------------------------------------ -// BufStream -//------------------------------------------------------------------------ - -BufStream::BufStream(Stream *strA, int bufSizeA): FilterStream(strA) { - bufSize = bufSizeA; - buf = (int *)gmallocn(bufSize, sizeof(int)); -} - -BufStream::~BufStream() { - gfree(buf); - delete str; -} - -Stream *BufStream::copy() { - return new BufStream(str->copy(), bufSize); -} - -void BufStream::reset() { - int i; - - str->reset(); - for (i = 0; i < bufSize; ++i) { - buf[i] = str->getChar(); - } -} - -int BufStream::getChar() { - int c, i; - - c = buf[0]; - for (i = 1; i < bufSize; ++i) { - buf[i-1] = buf[i]; - } - buf[bufSize - 1] = str->getChar(); - return c; -} - -int BufStream::lookChar() { - return buf[0]; -} - -int BufStream::lookChar(int idx) { - return buf[idx]; -} - -GBool BufStream::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -//------------------------------------------------------------------------ -// FixedLengthEncoder -//------------------------------------------------------------------------ - -FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA): - FilterStream(strA) { - length = lengthA; - count = 0; -} - -FixedLengthEncoder::~FixedLengthEncoder() { - if (str->isEncoder()) - delete str; -} - -Stream *FixedLengthEncoder::copy() { - error(errInternal, -1, "Called copy() on FixedLengthEncoder"); - return NULL; -} - -void FixedLengthEncoder::reset() { - str->reset(); - count = 0; -} - -int FixedLengthEncoder::getChar() { - if (length >= 0 && count >= length) - return EOF; - ++count; - return str->getChar(); -} - -int FixedLengthEncoder::lookChar() { - if (length >= 0 && count >= length) - return EOF; - return str->getChar(); -} - -GBool FixedLengthEncoder::isBinary(GBool last) { - return str->isBinary(gTrue); -} - -//------------------------------------------------------------------------ -// ASCIIHexEncoder -//------------------------------------------------------------------------ - -ASCIIHexEncoder::ASCIIHexEncoder(Stream *strA): - FilterStream(strA) { - bufPtr = bufEnd = buf; - lineLen = 0; - eof = gFalse; -} - -ASCIIHexEncoder::~ASCIIHexEncoder() { - if (str->isEncoder()) { - delete str; - } -} - -Stream *ASCIIHexEncoder::copy() { - error(errInternal, -1, "Called copy() on ASCIIHexEncoder"); - return NULL; -} - -void ASCIIHexEncoder::reset() { - str->reset(); - bufPtr = bufEnd = buf; - lineLen = 0; - eof = gFalse; -} - -GBool ASCIIHexEncoder::fillBuf() { - static const char *hex = "0123456789abcdef"; - int c; - - if (eof) { - return gFalse; - } - bufPtr = bufEnd = buf; - if ((c = str->getChar()) == EOF) { - *bufEnd++ = '>'; - eof = gTrue; - } else { - if (lineLen >= 64) { - *bufEnd++ = '\n'; - lineLen = 0; - } - *bufEnd++ = hex[(c >> 4) & 0x0f]; - *bufEnd++ = hex[c & 0x0f]; - lineLen += 2; - } - return gTrue; -} - -//------------------------------------------------------------------------ -// ASCII85Encoder -//------------------------------------------------------------------------ - -ASCII85Encoder::ASCII85Encoder(Stream *strA): - FilterStream(strA) { - bufPtr = bufEnd = buf; - lineLen = 0; - eof = gFalse; -} - -ASCII85Encoder::~ASCII85Encoder() { - if (str->isEncoder()) - delete str; -} - -Stream *ASCII85Encoder::copy() { - error(errInternal, -1, "Called copy() on ASCII85Encoder"); - return NULL; -} - -void ASCII85Encoder::reset() { - str->reset(); - bufPtr = bufEnd = buf; - lineLen = 0; - eof = gFalse; -} - -GBool ASCII85Encoder::fillBuf() { - Guint t; - char buf1[5]; - int c0, c1, c2, c3; - int n, i; - - if (eof) { - return gFalse; - } - c0 = str->getChar(); - c1 = str->getChar(); - c2 = str->getChar(); - c3 = str->getChar(); - bufPtr = bufEnd = buf; - if (c3 == EOF) { - if (c0 == EOF) { - n = 0; - t = 0; - } else { - if (c1 == EOF) { - n = 1; - t = c0 << 24; - } else if (c2 == EOF) { - n = 2; - t = (c0 << 24) | (c1 << 16); - } else { - n = 3; - t = (c0 << 24) | (c1 << 16) | (c2 << 8); - } - for (i = 4; i >= 0; --i) { - buf1[i] = (char)(t % 85 + 0x21); - t /= 85; - } - for (i = 0; i <= n; ++i) { - *bufEnd++ = buf1[i]; - if (++lineLen == 65) { - *bufEnd++ = '\n'; - lineLen = 0; - } - } - } - *bufEnd++ = '~'; - *bufEnd++ = '>'; - eof = gTrue; - } else { - t = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3; - if (t == 0) { - *bufEnd++ = 'z'; - if (++lineLen == 65) { - *bufEnd++ = '\n'; - lineLen = 0; - } - } else { - for (i = 4; i >= 0; --i) { - buf1[i] = (char)(t % 85 + 0x21); - t /= 85; - } - for (i = 0; i <= 4; ++i) { - *bufEnd++ = buf1[i]; - if (++lineLen == 65) { - *bufEnd++ = '\n'; - lineLen = 0; - } - } - } - } - return gTrue; -} - -//------------------------------------------------------------------------ -// RunLengthEncoder -//------------------------------------------------------------------------ - -RunLengthEncoder::RunLengthEncoder(Stream *strA): - FilterStream(strA) { - bufPtr = bufEnd = nextEnd = buf; - eof = gFalse; -} - -RunLengthEncoder::~RunLengthEncoder() { - if (str->isEncoder()) - delete str; -} - -Stream *RunLengthEncoder::copy() { - error(errInternal, -1, "Called copy() on RunLengthEncoder"); - return NULL; -} - -void RunLengthEncoder::reset() { - str->reset(); - bufPtr = bufEnd = nextEnd = buf; - eof = gFalse; -} - -// -// When fillBuf finishes, buf[] looks like this: -// +-----+--------------+-----------------+-- -// + tag | ... data ... | next 0, 1, or 2 | -// +-----+--------------+-----------------+-- -// ^ ^ ^ -// bufPtr bufEnd nextEnd -// -GBool RunLengthEncoder::fillBuf() { - int c, c1, c2; - int n; - - // already hit EOF? - if (eof) - return gFalse; - - // grab two bytes - if (nextEnd < bufEnd + 1) { - if ((c1 = str->getChar()) == EOF) { - eof = gTrue; - return gFalse; - } - } else { - c1 = bufEnd[0] & 0xff; - } - if (nextEnd < bufEnd + 2) { - if ((c2 = str->getChar()) == EOF) { - eof = gTrue; - buf[0] = 0; - buf[1] = (char)c1; - bufPtr = buf; - bufEnd = &buf[2]; - return gTrue; - } - } else { - c2 = bufEnd[1] & 0xff; - } - - // check for repeat - c = 0; // make gcc happy - if (c1 == c2) { - n = 2; - while (n < 128 && (c = str->getChar()) == c1) - ++n; - buf[0] = (char)(257 - n); - buf[1] = (char)c1; - bufEnd = &buf[2]; - if (c == EOF) { - eof = gTrue; - } else if (n < 128) { - buf[2] = (char)c; - nextEnd = &buf[3]; - } else { - nextEnd = bufEnd; - } - - // get up to 128 chars - } else { - buf[1] = (char)c1; - buf[2] = (char)c2; - n = 2; - while (n < 128) { - if ((c = str->getChar()) == EOF) { - eof = gTrue; - break; - } - ++n; - buf[n] = (char)c; - if (buf[n] == buf[n-1]) - break; - } - if (buf[n] == buf[n-1]) { - buf[0] = (char)(n-2-1); - bufEnd = &buf[n-1]; - nextEnd = &buf[n+1]; - } else { - buf[0] = (char)(n-1); - bufEnd = nextEnd = &buf[n+1]; - } - } - bufPtr = buf; - return gTrue; -} - -//------------------------------------------------------------------------ -// LZWEncoder -//------------------------------------------------------------------------ - -LZWEncoder::LZWEncoder(Stream *strA): - FilterStream(strA) -{ - inBufStart = 0; - inBufLen = 0; - outBufLen = 0; -} - -LZWEncoder::~LZWEncoder() { - if (str->isEncoder()) { - delete str; - } -} - -Stream *LZWEncoder::copy() { - error(errInternal, -1, "Called copy() on LZWEncoder"); - return NULL; -} - -void LZWEncoder::reset() { - int i; - - str->reset(); - - // initialize code table - for (i = 0; i < 256; ++i) { - table[i].byte = i; - table[i].next = NULL; - table[i].children = NULL; - } - nextSeq = 258; - codeLen = 9; - - // initialize input buffer - inBufLen = str->getBlock((char *)inBuf, sizeof(inBuf)); - inBufStart = 0; - - // initialize output buffer with a clear-table code - outBuf = 256; - outBufLen = 9; - needEOD = gFalse; -} - -int LZWEncoder::getChar() { - int ret; - - if (inBufLen == 0 && !needEOD && outBufLen == 0) { - return EOF; - } - if (outBufLen < 8 && (inBufLen > 0 || needEOD)) { - fillBuf(); - } - if (outBufLen >= 8) { - ret = (outBuf >> (outBufLen - 8)) & 0xff; - outBufLen -= 8; - } else { - ret = (outBuf << (8 - outBufLen)) & 0xff; - outBufLen = 0; - } - return ret; -} - -int LZWEncoder::lookChar() { - if (inBufLen == 0 && !needEOD && outBufLen == 0) { - return EOF; - } - if (outBufLen < 8 && (inBufLen > 0 || needEOD)) { - fillBuf(); - } - if (outBufLen >= 8) { - return (outBuf >> (outBufLen - 8)) & 0xff; - } else { - return (outBuf << (8 - outBufLen)) & 0xff; - } -} - -// On input, outBufLen < 8. -// This function generates, at most, 2 12-bit codes -// --> outBufLen < 8 + 12 + 12 = 32 -void LZWEncoder::fillBuf() { - LZWEncoderNode *p0, *p1; - int seqLen, code, i; - - if (needEOD) { - outBuf = (outBuf << codeLen) | 257; - outBufLen += codeLen; - needEOD = gFalse; - return; - } - - // find longest matching sequence (if any) - p0 = table + inBuf[inBufStart]; - seqLen = 1; - while (inBufLen > seqLen) { - for (p1 = p0->children; p1; p1 = p1->next) { - if (p1->byte == inBuf[inBufStart + seqLen]) { - break; - } - } - if (!p1) { - break; - } - p0 = p1; - ++seqLen; - } - code = (int)(p0 - table); - - // generate an output code - outBuf = (outBuf << codeLen) | code; - outBufLen += codeLen; - - // update the table - table[nextSeq].byte = seqLen < inBufLen ? inBuf[inBufStart + seqLen] : 0; - table[nextSeq].children = NULL; - if (table[code].children) { - table[nextSeq].next = table[code].children; - } else { - table[nextSeq].next = NULL; - } - table[code].children = table + nextSeq; - ++nextSeq; - - // update the input buffer - inBufStart += seqLen; - inBufLen -= seqLen; - if (inBufStart >= 4096 && inBufStart + inBufLen == sizeof(inBuf)) { - memcpy(inBuf, inBuf + inBufStart, inBufLen); - inBufStart = 0; - inBufLen += str->getBlock((char *)inBuf + inBufLen, - (int)sizeof(inBuf) - inBufLen); - } - - // increment codeLen; generate clear-table code - if (nextSeq == (1 << codeLen)) { - ++codeLen; - if (codeLen == 13) { - outBuf = (outBuf << 12) | 256; - outBufLen += 12; - for (i = 0; i < 256; ++i) { - table[i].next = NULL; - table[i].children = NULL; - } - nextSeq = 258; - codeLen = 9; - } - } - - // generate EOD next time - if (inBufLen == 0) { - needEOD = gTrue; - } -} diff --git a/test/bug-hunting/cve/CVE-2019-10025/Stream.h b/test/bug-hunting/cve/CVE-2019-10025/Stream.h deleted file mode 100644 index 3c036e9c34d..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10025/Stream.h +++ /dev/null @@ -1,1189 +0,0 @@ -//======================================================================== -// -// Stream.h -// -// Copyright 1996-2003 Glyph & Cog, LLC -// -//======================================================================== - -#ifndef STREAM_H -#define STREAM_H - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include -#if HAVE_JPEGLIB -#include -#include -#endif -#include "gtypes.h" -#include "gfile.h" -#include "Object.h" - -class BaseStream; -class SharedFile; - -//------------------------------------------------------------------------ - -enum StreamKind { - strFile, - strASCIIHex, - strASCII85, - strLZW, - strRunLength, - strCCITTFax, - strDCT, - strFlate, - strJBIG2, - strJPX, - strWeird // internal-use stream types -}; - -enum StreamColorSpaceMode { - streamCSNone, - streamCSDeviceGray, - streamCSDeviceRGB, - streamCSDeviceCMYK -}; - -//------------------------------------------------------------------------ - -// This is in Stream.h instead of Decrypt.h to avoid really annoying -// include file dependency loops. -enum CryptAlgorithm { - cryptRC4, - cryptAES, - cryptAES256 -}; - -//------------------------------------------------------------------------ -// Stream (base class) -//------------------------------------------------------------------------ - -class Stream { -public: - - // Constructor. - Stream(); - - // Destructor. - virtual ~Stream(); - - virtual Stream *copy() = 0; - - // Get kind of stream. - virtual StreamKind getKind() = 0; - - virtual GBool isEmbedStream() { - return gFalse; - } - - // Reset stream to beginning. - virtual void reset() = 0; - - // Close down the stream. - virtual void close(); - - // Get next char from stream. - virtual int getChar() = 0; - - // Peek at next char in stream. - virtual int lookChar() = 0; - - // Get next char from stream without using the predictor. - // This is only used by StreamPredictor. - virtual int getRawChar(); - - // Get exactly bytes from stream. Returns the number of - // bytes read -- the returned count will be less than at EOF. - virtual int getBlock(char *blk, int size); - - // Get next line from stream. - virtual char *getLine(char *buf, int size); - - // Discard the next bytes from stream. Returns the number of - // bytes discarded, which will be less than only if EOF is - // reached. - virtual Guint discardChars(Guint n); - - // Get current position in file. - virtual GFileOffset getPos() = 0; - - // Go to a position in the stream. If is negative, the - // position is from the end of the file; otherwise the position is - // from the start of the file. - virtual void setPos(GFileOffset pos, int dir = 0) = 0; - - // Get PostScript command for the filter(s). - virtual GString *getPSFilter(int psLevel, const char *indent); - - // Does this stream type potentially contain non-printable chars? - virtual GBool isBinary(GBool last = gTrue) = 0; - - // Get the BaseStream of this stream. - virtual BaseStream *getBaseStream() = 0; - - // Get the stream after the last decoder (this may be a BaseStream - // or a DecryptStream). - virtual Stream *getUndecodedStream() = 0; - - // Get the dictionary associated with this stream. - virtual Dict *getDict() = 0; - - // Is this an encoding filter? - virtual GBool isEncoder() { - return gFalse; - } - - // Get image parameters which are defined by the stream contents. - virtual void getImageParams(int *bitsPerComponent, - StreamColorSpaceMode *csMode) {} - - // Return the next stream in the "stack". - virtual Stream *getNextStream() { - return NULL; - } - - // Add filters to this stream according to the parameters in . - // Returns the new stream. - Stream *addFilters(Object *dict, int recursion = 0); - -private: - - Stream *makeFilter(char *name, Stream *str, Object *params, int recursion); -}; - -//------------------------------------------------------------------------ -// BaseStream -// -// This is the base class for all streams that read directly from a file. -//------------------------------------------------------------------------ - -class BaseStream : public Stream { -public: - - BaseStream(Object *dictA); - virtual ~BaseStream(); - virtual Stream *makeSubStream(GFileOffset start, GBool limited, - GFileOffset length, Object *dict) = 0; - virtual void setPos(GFileOffset pos, int dir = 0) = 0; - virtual GBool isBinary(GBool last = gTrue) { - return last; - } - virtual BaseStream *getBaseStream() { - return this; - } - virtual Stream *getUndecodedStream() { - return this; - } - virtual Dict *getDict() { - return dict.getDict(); - } - virtual GString *getFileName() { - return NULL; - } - - // Get/set position of first byte of stream within the file. - virtual GFileOffset getStart() = 0; - virtual void moveStart(int delta) = 0; - -protected: - - Object dict; -}; - -//------------------------------------------------------------------------ -// FilterStream -// -// This is the base class for all streams that filter another stream. -//------------------------------------------------------------------------ - -class FilterStream : public Stream { -public: - - FilterStream(Stream *strA); - virtual ~FilterStream(); - virtual void close(); - virtual GFileOffset getPos() { - return str->getPos(); - } - virtual void setPos(GFileOffset pos, int dir = 0); - virtual BaseStream *getBaseStream() { - return str->getBaseStream(); - } - virtual Stream *getUndecodedStream() { - return str->getUndecodedStream(); - } - virtual Dict *getDict() { - return str->getDict(); - } - virtual Stream *getNextStream() { - return str; - } - -protected: - - Stream *str; -}; - -//------------------------------------------------------------------------ -// ImageStream -//------------------------------------------------------------------------ - -class ImageStream { -public: - - // Create an image stream object for an image with the specified - // parameters. Note that these are the actual image parameters, - // which may be different from the predictor parameters. - ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA); - - ~ImageStream(); - - // Reset the stream. - void reset(); - - // Close down the stream. - void close(); - - // Gets the next pixel from the stream. should be able to hold - // at least nComps elements. Returns false at end of file. - GBool getPixel(Guchar *pix); - - // Returns a pointer to the next line of pixels. Returns NULL at - // end of file. - Guchar *getLine(); - - // Skip an entire line from the image. - void skipLine(); - -private: - - Stream *str; // base stream - int width; // pixels per line - int nComps; // components per pixel - int nBits; // bits per component - int nVals; // components per line - int inputLineSize; // input line buffer size - char *inputLine; // input line buffer - Guchar *imgLine; // line buffer - int imgIdx; // current index in imgLine -}; - - -//------------------------------------------------------------------------ -// StreamPredictor -//------------------------------------------------------------------------ - -class StreamPredictor { -public: - - // Create a predictor object. Note that the parameters are for the - // predictor, and may not match the actual image parameters. - StreamPredictor(Stream *strA, int predictorA, - int widthA, int nCompsA, int nBitsA); - - ~StreamPredictor(); - - GBool isOk() { - return ok; - } - - void reset(); - - int lookChar(); - int getChar(); - int getBlock(char *blk, int size); - - int getPredictor() { - return predictor; - } - int getWidth() { - return width; - } - int getNComps() { - return nComps; - } - int getNBits() { - return nBits; - } - -private: - - GBool getNextLine(); - - Stream *str; // base stream - int predictor; // predictor - int width; // pixels per line - int nComps; // components per pixel - int nBits; // bits per component - int nVals; // components per line - int pixBytes; // bytes per pixel - int rowBytes; // bytes per line - Guchar *predLine; // line buffer - int predIdx; // current index in predLine - GBool ok; -}; - -//------------------------------------------------------------------------ -// FileStream -//------------------------------------------------------------------------ - -#define fileStreamBufSize 256 - -class FileStream : public BaseStream { -public: - - FileStream(FILE *fA, GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA); - virtual ~FileStream(); - virtual Stream *copy(); - virtual Stream *makeSubStream(GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA); - virtual StreamKind getKind() { - return strFile; - } - virtual void reset(); - virtual int getChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); - } - virtual int lookChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); - } - virtual int getBlock(char *blk, int size); - virtual GFileOffset getPos() { - return bufPos + (int)(bufPtr - buf); - } - virtual void setPos(GFileOffset pos, int dir = 0); - virtual GFileOffset getStart() { - return start; - } - virtual void moveStart(int delta); - -private: - - FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA, - GFileOffset lengthA, Object *dictA); - GBool fillBuf(); - - SharedFile *f; - GFileOffset start; - GBool limited; - GFileOffset length; - char buf[fileStreamBufSize]; - char *bufPtr; - char *bufEnd; - GFileOffset bufPos; -}; - -//------------------------------------------------------------------------ -// MemStream -//------------------------------------------------------------------------ - -class MemStream : public BaseStream { -public: - - MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA); - virtual ~MemStream(); - virtual Stream *copy(); - virtual Stream *makeSubStream(GFileOffset start, GBool limited, - GFileOffset lengthA, Object *dictA); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual void close(); - virtual int getChar() - { - return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; - } - virtual int lookChar() - { - return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; - } - virtual int getBlock(char *blk, int size); - virtual GFileOffset getPos() { - return (GFileOffset)(bufPtr - buf); - } - virtual void setPos(GFileOffset pos, int dir = 0); - virtual GFileOffset getStart() { - return start; - } - virtual void moveStart(int delta); - -private: - - char *buf; - Guint start; - Guint length; - char *bufEnd; - char *bufPtr; - GBool needFree; -}; - -//------------------------------------------------------------------------ -// EmbedStream -// -// This is a special stream type used for embedded streams (inline -// images). It reads directly from the base stream -- after the -// EmbedStream is deleted, reads from the base stream will proceed where -// the BaseStream left off. Note that this is very different behavior -// that creating a new FileStream (using makeSubStream). -//------------------------------------------------------------------------ - -class EmbedStream : public BaseStream { -public: - - EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA); - virtual ~EmbedStream(); - virtual Stream *copy(); - virtual Stream *makeSubStream(GFileOffset start, GBool limitedA, - GFileOffset lengthA, Object *dictA); - virtual StreamKind getKind() { - return str->getKind(); - } - virtual GBool isEmbedStream() { - return gTrue; - } - virtual void reset() {} - virtual int getChar(); - virtual int lookChar(); - virtual int getBlock(char *blk, int size); - virtual GFileOffset getPos() { - return str->getPos(); - } - virtual void setPos(GFileOffset pos, int dir = 0); - virtual GFileOffset getStart(); - virtual void moveStart(int delta); - -private: - - Stream *str; - GBool limited; - GFileOffset length; -}; - -//------------------------------------------------------------------------ -// ASCIIHexStream -//------------------------------------------------------------------------ - -class ASCIIHexStream : public FilterStream { -public: - - ASCIIHexStream(Stream *strA); - virtual ~ASCIIHexStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strASCIIHex; - } - virtual void reset(); - virtual int getChar() - { - int c = lookChar(); buf = EOF; return c; - } - virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - int buf; - GBool eof; -}; - -//------------------------------------------------------------------------ -// ASCII85Stream -//------------------------------------------------------------------------ - -class ASCII85Stream : public FilterStream { -public: - - ASCII85Stream(Stream *strA); - virtual ~ASCII85Stream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strASCII85; - } - virtual void reset(); - virtual int getChar() - { - int ch = lookChar(); ++index; return ch; - } - virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - int c[5]; - int b[4]; - int index, n; - GBool eof; -}; - -//------------------------------------------------------------------------ -// LZWStream -//------------------------------------------------------------------------ - -class LZWStream : public FilterStream { -public: - - LZWStream(Stream *strA, int predictor, int columns, int colors, - int bits, int earlyA); - virtual ~LZWStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strLZW; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual int getRawChar(); - virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - StreamPredictor *pred; // predictor - int early; // early parameter - GBool eof; // true if at eof - int inputBuf; // input buffer - int inputBits; // number of bits in input buffer - struct { // decoding table - int length; - int head; - Guchar tail; - } table[4097]; - int nextCode; // next code to be used - int nextBits; // number of bits in next code word - int prevCode; // previous code used in stream - int newChar; // next char to be added to table - Guchar seqBuf[4097]; // buffer for current sequence - int seqLength; // length of current sequence - int seqIndex; // index into current sequence - GBool first; // first code after a table clear - - GBool processNextCode(); - void clearTable(); - int getCode(); -}; - -//------------------------------------------------------------------------ -// RunLengthStream -//------------------------------------------------------------------------ - -class RunLengthStream : public FilterStream { -public: - - RunLengthStream(Stream *strA); - virtual ~RunLengthStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strRunLength; - } - virtual void reset(); - virtual int getChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); - } - virtual int lookChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); - } - virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - char buf[128]; // buffer - char *bufPtr; // next char to read - char *bufEnd; // end of buffer - GBool eof; - - GBool fillBuf(); -}; - -//------------------------------------------------------------------------ -// CCITTFaxStream -//------------------------------------------------------------------------ - -struct CCITTCodeTable; - -class CCITTFaxStream : public FilterStream { -public: - - CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, - GBool byteAlignA, int columnsA, int rowsA, - GBool endOfBlockA, GBool blackA); - virtual ~CCITTFaxStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strCCITTFax; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - int encoding; // 'K' parameter - GBool endOfLine; // 'EndOfLine' parameter - GBool byteAlign; // 'EncodedByteAlign' parameter - int columns; // 'Columns' parameter - int rows; // 'Rows' parameter - GBool endOfBlock; // 'EndOfBlock' parameter - GBool black; // 'BlackIs1' parameter - int blackXOR; - GBool eof; // true if at eof - GBool nextLine2D; // true if next line uses 2D encoding - int row; // current row - Guint inputBuf; // input buffer - int inputBits; // number of bits in input buffer - int *codingLine; // coding line changing elements - int *refLine; // reference line changing elements - int nextCol; // next column to read - int a0i; // index into codingLine - GBool err; // error on current line - int nErrors; // number of errors so far in this stream - - void addPixels(int a1, int blackPixels); - void addPixelsNeg(int a1, int blackPixels); - GBool readRow(); - short getTwoDimCode(); - short getWhiteCode(); - short getBlackCode(); - short lookBits(int n); - void eatBits(int n) { - if ((inputBits -= n) < 0) inputBits = 0; - } -}; - -//------------------------------------------------------------------------ -// DCTStream -//------------------------------------------------------------------------ - -#if HAVE_JPEGLIB - -class DCTStream; - -#define dctStreamBufSize 4096 - -struct DCTSourceMgr { - jpeg_source_mgr src; - DCTStream *str; - char buf[dctStreamBufSize]; -}; - -struct DCTErrorMgr { - struct jpeg_error_mgr err; - jmp_buf setjmpBuf; -}; - -#else // HAVE_JPEGLIB - -// DCT component info -struct DCTCompInfo { - int id; // component ID - int hSample, vSample; // horiz/vert sampling resolutions - int quantTable; // quantization table number - int prevDC; // DC coefficient accumulator -}; - -struct DCTScanInfo { - GBool comp[4]; // comp[i] is set if component i is - // included in this scan - int numComps; // number of components in the scan - int dcHuffTable[4]; // DC Huffman table numbers - int acHuffTable[4]; // AC Huffman table numbers - int firstCoeff, lastCoeff; // first and last DCT coefficient - int ah, al; // successive approximation parameters -}; - -// DCT Huffman decoding table -struct DCTHuffTable { - Guchar firstSym[17]; // first symbol for this bit length - Gushort firstCode[17]; // first code for this bit length - Gushort numCodes[17]; // number of codes of this bit length - Guchar sym[256]; // symbols -}; - -#endif // HAVE_JPEGLIB - -class DCTStream : public FilterStream { -public: - - DCTStream(Stream *strA, int colorXformA); - virtual ~DCTStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strDCT; - } - virtual void reset(); - virtual void close(); - virtual int getChar(); - virtual int lookChar(); - virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - Stream *getRawStream() { - return str; - } - -private: - -#if HAVE_JPEGLIB - - int colorXform; // color transform: -1 = unspecified - // 0 = none - // 1 = YUV/YUVK -> RGB/CMYK - jpeg_decompress_struct decomp; - DCTErrorMgr errorMgr; - DCTSourceMgr sourceMgr; - GBool error; - char *lineBuf; - int lineBufHeight; - char *lineBufRows[4]; - char *bufPtr; - char *bufEnd; - GBool inlineImage; - - GBool fillBuf(); - static void errorExit(j_common_ptr d); - static void errorMessage(j_common_ptr d); - static void initSourceCbk(j_decompress_ptr d); - static boolean fillInputBufferCbk(j_decompress_ptr d); - static void skipInputDataCbk(j_decompress_ptr d, long numBytes); - static void termSourceCbk(j_decompress_ptr d); - -#else // HAVE_JPEGLIB - - GBool progressive; // set if in progressive mode - GBool interleaved; // set if in interleaved mode - int width, height; // image size - int mcuWidth, mcuHeight; // size of min coding unit, in data units - int bufWidth, bufHeight; // frameBuf size - DCTCompInfo compInfo[4]; // info for each component - DCTScanInfo scanInfo; // info for the current scan - int numComps; // number of components in image - int colorXform; // color transform: -1 = unspecified - // 0 = none - // 1 = YUV/YUVK -> RGB/CMYK - GBool gotJFIFMarker; // set if APP0 JFIF marker was present - GBool gotAdobeMarker; // set if APP14 Adobe marker was present - int restartInterval; // restart interval, in MCUs - Gushort quantTables[4][64]; // quantization tables - int numQuantTables; // number of quantization tables - DCTHuffTable dcHuffTables[4]; // DC Huffman tables - DCTHuffTable acHuffTables[4]; // AC Huffman tables - int numDCHuffTables; // number of DC Huffman tables - int numACHuffTables; // number of AC Huffman tables - Guchar *rowBuf; - Guchar *rowBufPtr; // current position within rowBuf - Guchar *rowBufEnd; // end of valid data in rowBuf - int *frameBuf[4]; // buffer for frame (progressive mode) - int comp, x, y; // current position within image/MCU - int restartCtr; // MCUs left until restart - int restartMarker; // next restart marker - int eobRun; // number of EOBs left in the current run - int inputBuf; // input buffer for variable length codes - int inputBits; // number of valid bits in input buffer - - void restart(); - GBool readMCURow(); - void readScan(); - GBool readDataUnit(DCTHuffTable *dcHuffTable, - DCTHuffTable *acHuffTable, - int *prevDC, int data[64]); - GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable, - DCTHuffTable *acHuffTable, - int *prevDC, int data[64]); - void decodeImage(); - void transformDataUnit(Gushort *quantTable, - int dataIn[64], Guchar dataOut[64]); - int readHuffSym(DCTHuffTable *table); - int readAmp(int size); - int readBit(); - GBool readHeader(GBool frame); - GBool readBaselineSOF(); - GBool readProgressiveSOF(); - GBool readScanInfo(); - GBool readQuantTables(); - GBool readHuffmanTables(); - GBool readRestartInterval(); - GBool readJFIFMarker(); - GBool readAdobeMarker(); - GBool readTrailer(); - int readMarker(); - int read16(); - -#endif // HAVE_JPEGLIB -}; - -//------------------------------------------------------------------------ -// FlateStream -//------------------------------------------------------------------------ - -#define flateWindow 32768 // buffer size -#define flateMask (flateWindow-1) -#define flateMaxHuffman 15 // max Huffman code length -#define flateMaxCodeLenCodes 19 // max # code length codes -#define flateMaxLitCodes 288 // max # literal codes -#define flateMaxDistCodes 30 // max # distance codes - -// Huffman code table entry -struct FlateCode { - Gushort len; // code length, in bits - Gushort val; // value represented by this code -}; - -struct FlateHuffmanTab { - FlateCode *codes; - int maxLen; -}; - -// Decoding info for length and distance code words -struct FlateDecode { - int bits; // # extra bits - int first; // first length/distance -}; - -class FlateStream : public FilterStream { -public: - - FlateStream(Stream *strA, int predictor, int columns, - int colors, int bits); - virtual ~FlateStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strFlate; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual int getRawChar(); - virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); - virtual GBool isBinary(GBool last = gTrue); - -private: - - StreamPredictor *pred; // predictor - Guchar buf[flateWindow]; // output data buffer - int index; // current index into output buffer - int remain; // number valid bytes in output buffer - int codeBuf; // input buffer - int codeSize; // number of bits in input buffer - int // literal and distance code lengths - codeLengths[flateMaxLitCodes + flateMaxDistCodes]; - FlateHuffmanTab litCodeTab; // literal code table - FlateHuffmanTab distCodeTab; // distance code table - GBool compressedBlock; // set if reading a compressed block - int blockLen; // remaining length of uncompressed block - GBool endOfBlock; // set when end of block is reached - GBool eof; // set when end of stream is reached - - static int // code length code reordering - codeLenCodeMap[flateMaxCodeLenCodes]; - static FlateDecode // length decoding info - lengthDecode[flateMaxLitCodes-257]; - static FlateDecode // distance decoding info - distDecode[flateMaxDistCodes]; - static FlateHuffmanTab // fixed literal code table - fixedLitCodeTab; - static FlateHuffmanTab // fixed distance code table - fixedDistCodeTab; - - void readSome(); - GBool startBlock(); - void loadFixedCodes(); - GBool readDynamicCodes(); - void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab); - int getHuffmanCodeWord(FlateHuffmanTab *tab); - int getCodeWord(int bits); -}; - -//------------------------------------------------------------------------ -// EOFStream -//------------------------------------------------------------------------ - -class EOFStream : public FilterStream { -public: - - EOFStream(Stream *strA); - virtual ~EOFStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset() {} - virtual int getChar() { - return EOF; - } - virtual int lookChar() { - return EOF; - } - virtual int getBlock(char *blk, int size) { - return 0; - } - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue) { - return gFalse; - } -}; - -//------------------------------------------------------------------------ -// BufStream -//------------------------------------------------------------------------ - -class BufStream : public FilterStream { -public: - - BufStream(Stream *strA, int bufSizeA); - virtual ~BufStream(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue); - - int lookChar(int idx); - -private: - - int *buf; - int bufSize; -}; - -//------------------------------------------------------------------------ -// FixedLengthEncoder -//------------------------------------------------------------------------ - -class FixedLengthEncoder : public FilterStream { -public: - - FixedLengthEncoder(Stream *strA, int lengthA); - ~FixedLengthEncoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue); - virtual GBool isEncoder() { - return gTrue; - } - -private: - - int length; - int count; -}; - -//------------------------------------------------------------------------ -// ASCIIHexEncoder -//------------------------------------------------------------------------ - -class ASCIIHexEncoder : public FilterStream { -public: - - ASCIIHexEncoder(Stream *strA); - virtual ~ASCIIHexEncoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); - } - virtual int lookChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); - } - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue) { - return gFalse; - } - virtual GBool isEncoder() { - return gTrue; - } - -private: - - char buf[4]; - char *bufPtr; - char *bufEnd; - int lineLen; - GBool eof; - - GBool fillBuf(); -}; - -//------------------------------------------------------------------------ -// ASCII85Encoder -//------------------------------------------------------------------------ - -class ASCII85Encoder : public FilterStream { -public: - - ASCII85Encoder(Stream *strA); - virtual ~ASCII85Encoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); - } - virtual int lookChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); - } - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue) { - return gFalse; - } - virtual GBool isEncoder() { - return gTrue; - } - -private: - - char buf[8]; - char *bufPtr; - char *bufEnd; - int lineLen; - GBool eof; - - GBool fillBuf(); -}; - -//------------------------------------------------------------------------ -// RunLengthEncoder -//------------------------------------------------------------------------ - -class RunLengthEncoder : public FilterStream { -public: - - RunLengthEncoder(Stream *strA); - virtual ~RunLengthEncoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); - } - virtual int lookChar() - { - return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); - } - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue) { - return gTrue; - } - virtual GBool isEncoder() { - return gTrue; - } - -private: - - char buf[131]; - char *bufPtr; - char *bufEnd; - char *nextEnd; - GBool eof; - - GBool fillBuf(); -}; - -//------------------------------------------------------------------------ -// LZWEncoder -//------------------------------------------------------------------------ - -struct LZWEncoderNode { - int byte; - LZWEncoderNode *next; // next sibling - LZWEncoderNode *children; // first child -}; - -class LZWEncoder : public FilterStream { -public: - - LZWEncoder(Stream *strA); - virtual ~LZWEncoder(); - virtual Stream *copy(); - virtual StreamKind getKind() { - return strWeird; - } - virtual void reset(); - virtual int getChar(); - virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent) - { - return NULL; - } - virtual GBool isBinary(GBool last = gTrue) { - return gTrue; - } - virtual GBool isEncoder() { - return gTrue; - } - -private: - - LZWEncoderNode table[4096]; - int nextSeq; - int codeLen; - Guchar inBuf[8192]; - int inBufStart; - int inBufLen; - int outBuf; - int outBufLen; - GBool needEOD; - - void fillBuf(); -}; - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-10025/expected.txt b/test/bug-hunting/cve/CVE-2019-10025/expected.txt deleted file mode 100644 index c9f5498f13d..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10025/expected.txt +++ /dev/null @@ -1 +0,0 @@ -Stream.cc:360:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-10026/Function.cc b/test/bug-hunting/cve/CVE-2019-10026/Function.cc deleted file mode 100644 index 72cadd9bed1..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10026/Function.cc +++ /dev/null @@ -1,1567 +0,0 @@ -//======================================================================== -// -// Function.cc -// -// Copyright 2001-2003 Glyph & Cog, LLC -// -//======================================================================== - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include -#include -#include -#include -#include "gmem.h" -#include "gmempp.h" -#include "GList.h" -#include "Object.h" -#include "Dict.h" -#include "Stream.h" -#include "Error.h" -#include "Function.h" - -//------------------------------------------------------------------------ - -// Max depth of nested functions. This is used to catch infinite -// loops in the function object structure. -#define recursionLimit 8 - -//------------------------------------------------------------------------ -// Function -//------------------------------------------------------------------------ - -Function::Function() { -} - -Function::~Function() { -} - -Function *Function::parse(Object *funcObj, int recursion) { - Function *func; - Dict *dict; - int funcType; - Object obj1; - - if (recursion > recursionLimit) { - error(errSyntaxError, -1, "Loop detected in function objects"); - return NULL; - } - - if (funcObj->isStream()) { - dict = funcObj->streamGetDict(); - } else if (funcObj->isDict()) { - dict = funcObj->getDict(); - } else if (funcObj->isName("Identity")) { - return new IdentityFunction(); - } else { - error(errSyntaxError, -1, "Expected function dictionary or stream"); - return NULL; - } - - if (!dict->lookup("FunctionType", &obj1)->isInt()) { - error(errSyntaxError, -1, "Function type is missing or wrong type"); - obj1.free(); - return NULL; - } - funcType = obj1.getInt(); - obj1.free(); - - if (funcType == 0) { - func = new SampledFunction(funcObj, dict); - } else if (funcType == 2) { - func = new ExponentialFunction(funcObj, dict); - } else if (funcType == 3) { - func = new StitchingFunction(funcObj, dict, recursion); - } else if (funcType == 4) { - func = new PostScriptFunction(funcObj, dict); - } else { - error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType); - return NULL; - } - if (!func->isOk()) { - delete func; - return NULL; - } - - return func; -} - -GBool Function::init(Dict *dict) { - Object obj1, obj2; - int i; - - //----- Domain - if (!dict->lookup("Domain", &obj1)->isArray()) { - error(errSyntaxError, -1, "Function is missing domain"); - goto err2; - } - m = obj1.arrayGetLength() / 2; - if (m > funcMaxInputs) { - error(errSyntaxError, -1, - "Functions with more than {0:d} inputs are unsupported", - funcMaxInputs); - goto err2; - } - for (i = 0; i < m; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function domain array"); - goto err1; - } - domain[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function domain array"); - goto err1; - } - domain[i][1] = obj2.getNum(); - obj2.free(); - } - obj1.free(); - - //----- Range - hasRange = gFalse; - n = 0; - if (dict->lookup("Range", &obj1)->isArray()) { - hasRange = gTrue; - n = obj1.arrayGetLength() / 2; - if (n > funcMaxOutputs) { - error(errSyntaxError, -1, - "Functions with more than {0:d} outputs are unsupported", - funcMaxOutputs); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function range array"); - goto err1; - } - range[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function range array"); - goto err1; - } - range[i][1] = obj2.getNum(); - obj2.free(); - } - } - obj1.free(); - - return gTrue; - - err1: - obj2.free(); - err2: - obj1.free(); - return gFalse; -} - -//------------------------------------------------------------------------ -// IdentityFunction -//------------------------------------------------------------------------ - -IdentityFunction::IdentityFunction() { - int i; - - // fill these in with arbitrary values just in case they get used - // somewhere - m = funcMaxInputs; - n = funcMaxOutputs; - for (i = 0; i < funcMaxInputs; ++i) { - domain[i][0] = 0; - domain[i][1] = 1; - } - hasRange = gFalse; -} - -IdentityFunction::~IdentityFunction() { -} - -void IdentityFunction::transform(double *in, double *out) { - int i; - - for (i = 0; i < funcMaxOutputs; ++i) { - out[i] = in[i]; - } -} - -//------------------------------------------------------------------------ -// SampledFunction -//------------------------------------------------------------------------ - -SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { - Stream *str; - int sampleBits; - double sampleMul; - Object obj1, obj2; - Guint buf, bitMask; - int bits; - Guint s; - double in[funcMaxInputs]; - int i, j, t, bit, idx; - - idxOffset = NULL; - samples = NULL; - sBuf = NULL; - ok = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (!hasRange) { - error(errSyntaxError, -1, "Type 0 function is missing range"); - goto err1; - } - if (m > sampledFuncMaxInputs) { - error(errSyntaxError, -1, - "Sampled functions with more than {0:d} inputs are unsupported", - sampledFuncMaxInputs); - goto err1; - } - - //----- buffer - sBuf = (double *)gmallocn(1 << m, sizeof(double)); - - //----- get the stream - if (!funcObj->isStream()) { - error(errSyntaxError, -1, "Type 0 function isn't a stream"); - goto err1; - } - str = funcObj->getStream(); - - //----- Size - if (!dict->lookup("Size", &obj1)->isArray() || - obj1.arrayGetLength() != m) { - error(errSyntaxError, -1, "Function has missing or invalid size array"); - goto err2; - } - for (i = 0; i < m; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isInt()) { - error(errSyntaxError, -1, "Illegal value in function size array"); - goto err3; - } - sampleSize[i] = obj2.getInt(); - if (sampleSize[i] <= 0) { - error(errSyntaxError, -1, "Illegal non-positive value in function size array"); - goto err3; - } - obj2.free(); - } - obj1.free(); - idxOffset = (int *)gmallocn(1 << m, sizeof(int)); - for (i = 0; i < (1<= 1; --j, t <<= 1) { - if (sampleSize[j] == 1) { - bit = 0; - } else { - bit = (t >> (m - 1)) & 1; - } - idx = (idx + bit) * sampleSize[j-1]; - } - if (sampleSize[0] == 1) { - bit = 0; - } else { - bit = (t >> (m - 1)) & 1; - } - idxOffset[i] = (idx + bit) * n; - } - - //----- BitsPerSample - if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { - error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample"); - goto err2; - } - sampleBits = obj1.getInt(); - sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1); - obj1.free(); - - //----- Encode - if (dict->lookup("Encode", &obj1)->isArray() && - obj1.arrayGetLength() == 2*m) { - for (i = 0; i < m; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function encode array"); - goto err3; - } - encode[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function encode array"); - goto err3; - } - encode[i][1] = obj2.getNum(); - obj2.free(); - } - } else { - for (i = 0; i < m; ++i) { - encode[i][0] = 0; - encode[i][1] = sampleSize[i] - 1; - } - } - obj1.free(); - for (i = 0; i < m; ++i) { - inputMul[i] = (encode[i][1] - encode[i][0]) / - (domain[i][1] - domain[i][0]); - } - - //----- Decode - if (dict->lookup("Decode", &obj1)->isArray() && - obj1.arrayGetLength() == 2*n) { - for (i = 0; i < n; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function decode array"); - goto err3; - } - decode[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function decode array"); - goto err3; - } - decode[i][1] = obj2.getNum(); - obj2.free(); - } - } else { - for (i = 0; i < n; ++i) { - decode[i][0] = range[i][0]; - decode[i][1] = range[i][1]; - } - } - obj1.free(); - - //----- samples - nSamples = n; - for (i = 0; i < m; ++i) - nSamples *= sampleSize[i]; - samples = (double *)gmallocn(nSamples, sizeof(double)); - buf = 0; - bits = 0; - bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU; - str->reset(); - for (i = 0; i < nSamples; ++i) { - if (sampleBits == 8) { - s = str->getChar(); - } else if (sampleBits == 16) { - s = str->getChar(); - s = (s << 8) + str->getChar(); - } else if (sampleBits == 32) { - s = str->getChar(); - s = (s << 8) + str->getChar(); - s = (s << 8) + str->getChar(); - s = (s << 8) + str->getChar(); - } else { - while (bits < sampleBits) { - buf = (buf << 8) | (str->getChar() & 0xff); - bits += 8; - } - s = (buf >> (bits - sampleBits)) & bitMask; - bits -= sampleBits; - } - samples[i] = (double)s * sampleMul; - } - str->close(); - - // set up the cache - for (i = 0; i < m; ++i) { - in[i] = domain[i][0]; - cacheIn[i] = in[i] - 1; - } - transform(in, cacheOut); - - ok = gTrue; - return; - - err3: - obj2.free(); - err2: - obj1.free(); - err1: - return; -} - -SampledFunction::~SampledFunction() { - if (idxOffset) { - gfree(idxOffset); - } - if (samples) { - gfree(samples); - } - if (sBuf) { - gfree(sBuf); - } -} - -SampledFunction::SampledFunction(SampledFunction *func) { - memcpy((void *)this, (void *)func, sizeof(SampledFunction)); - idxOffset = (int *)gmallocn(1 << m, sizeof(int)); - memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int)); - samples = (double *)gmallocn(nSamples, sizeof(double)); - memcpy(samples, func->samples, nSamples * sizeof(double)); - sBuf = (double *)gmallocn(1 << m, sizeof(double)); -} - -void SampledFunction::transform(double *in, double *out) { - double x; - int e[funcMaxInputs]; - double efrac0[funcMaxInputs]; - double efrac1[funcMaxInputs]; - int i, j, k, idx0, t; - - // check the cache - for (i = 0; i < m; ++i) { - if (in[i] != cacheIn[i]) { - break; - } - } - if (i == m) { - for (i = 0; i < n; ++i) { - out[i] = cacheOut[i]; - } - return; - } - - // map input values into sample array - for (i = 0; i < m; ++i) { - x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0]; - if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x) - x = 0; - } else if (x > sampleSize[i] - 1) { - x = sampleSize[i] - 1; - } - e[i] = (int)x; - if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) { - // this happens if in[i] = domain[i][1] - e[i] = sampleSize[i] - 2; - } - efrac1[i] = x - e[i]; - efrac0[i] = 1 - efrac1[i]; - } - - // compute index for the first sample to be used - idx0 = 0; - for (k = m - 1; k >= 1; --k) { - idx0 = (idx0 + e[k]) * sampleSize[k-1]; - } - idx0 = (idx0 + e[0]) * n; - - // for each output, do m-linear interpolation - for (i = 0; i < n; ++i) { - - // pull 2^m values out of the sample array - for (j = 0; j < (1<>= 1) { - for (k = 0; k < t; k += 2) { - sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1]; - } - } - - // map output value to range - out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]; - if (out[i] < range[i][0]) { - out[i] = range[i][0]; - } else if (out[i] > range[i][1]) { - out[i] = range[i][1]; - } - } - - // save current result in the cache - for (i = 0; i < m; ++i) { - cacheIn[i] = in[i]; - } - for (i = 0; i < n; ++i) { - cacheOut[i] = out[i]; - } -} - -//------------------------------------------------------------------------ -// ExponentialFunction -//------------------------------------------------------------------------ - -ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { - Object obj1, obj2; - int i; - - ok = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (m != 1) { - error(errSyntaxError, -1, "Exponential function with more than one input"); - goto err1; - } - - //----- C0 - if (dict->lookup("C0", &obj1)->isArray()) { - if (hasRange && obj1.arrayGetLength() != n) { - error(errSyntaxError, -1, "Function's C0 array is wrong length"); - goto err2; - } - n = obj1.arrayGetLength(); - if (n > funcMaxOutputs) { - error(errSyntaxError, -1, - "Functions with more than {0:d} outputs are unsupported", - funcMaxOutputs); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function C0 array"); - goto err3; - } - c0[i] = obj2.getNum(); - obj2.free(); - } - } else { - if (hasRange && n != 1) { - error(errSyntaxError, -1, "Function's C0 array is wrong length"); - goto err2; - } - n = 1; - c0[0] = 0; - } - obj1.free(); - - //----- C1 - if (dict->lookup("C1", &obj1)->isArray()) { - if (obj1.arrayGetLength() != n) { - error(errSyntaxError, -1, "Function's C1 array is wrong length"); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isNum()) { - error(errSyntaxError, -1, "Illegal value in function C1 array"); - goto err3; - } - c1[i] = obj2.getNum(); - obj2.free(); - } - } else { - if (n != 1) { - error(errSyntaxError, -1, "Function's C1 array is wrong length"); - goto err2; - } - c1[0] = 1; - } - obj1.free(); - - //----- N (exponent) - if (!dict->lookup("N", &obj1)->isNum()) { - error(errSyntaxError, -1, "Function has missing or invalid N"); - goto err2; - } - e = obj1.getNum(); - obj1.free(); - - ok = gTrue; - return; - - err3: - obj2.free(); - err2: - obj1.free(); - err1: - return; -} - -ExponentialFunction::~ExponentialFunction() { -} - -ExponentialFunction::ExponentialFunction(ExponentialFunction *func) { - memcpy((void *)this, (void *)func, sizeof(ExponentialFunction)); -} - -void ExponentialFunction::transform(double *in, double *out) { - double x; - int i; - - if (in[0] < domain[0][0]) { - x = domain[0][0]; - } else if (in[0] > domain[0][1]) { - x = domain[0][1]; - } else { - x = in[0]; - } - for (i = 0; i < n; ++i) { - out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]); - if (hasRange) { - if (out[i] < range[i][0]) { - out[i] = range[i][0]; - } else if (out[i] > range[i][1]) { - out[i] = range[i][1]; - } - } - } - return; -} - -//------------------------------------------------------------------------ -// StitchingFunction -//------------------------------------------------------------------------ - -StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, - int recursion) { - Object obj1, obj2; - int i; - - ok = gFalse; - funcs = NULL; - bounds = NULL; - encode = NULL; - scale = NULL; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (m != 1) { - error(errSyntaxError, -1, "Stitching function with more than one input"); - goto err1; - } - - //----- Functions - if (!dict->lookup("Functions", &obj1)->isArray()) { - error(errSyntaxError, -1, - "Missing 'Functions' entry in stitching function"); - goto err1; - } - k = obj1.arrayGetLength(); - funcs = (Function **)gmallocn(k, sizeof(Function *)); - bounds = (double *)gmallocn(k + 1, sizeof(double)); - encode = (double *)gmallocn(2 * k, sizeof(double)); - scale = (double *)gmallocn(k, sizeof(double)); - for (i = 0; i < k; ++i) { - funcs[i] = NULL; - } - for (i = 0; i < k; ++i) { - if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2), - recursion + 1))) { - goto err2; - } - if (funcs[i]->getInputSize() != 1 || - (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { - error(errSyntaxError, -1, - "Incompatible subfunctions in stitching function"); - goto err2; - } - obj2.free(); - } - obj1.free(); - - //----- Bounds - if (!dict->lookup("Bounds", &obj1)->isArray() || - obj1.arrayGetLength() != k - 1) { - error(errSyntaxError, -1, - "Missing or invalid 'Bounds' entry in stitching function"); - goto err1; - } - bounds[0] = domain[0][0]; - for (i = 1; i < k; ++i) { - if (!obj1.arrayGet(i - 1, &obj2)->isNum()) { - error(errSyntaxError, -1, - "Invalid type in 'Bounds' array in stitching function"); - goto err2; - } - bounds[i] = obj2.getNum(); - obj2.free(); - } - bounds[k] = domain[0][1]; - obj1.free(); - - //----- Encode - if (!dict->lookup("Encode", &obj1)->isArray() || - obj1.arrayGetLength() != 2 * k) { - error(errSyntaxError, -1, - "Missing or invalid 'Encode' entry in stitching function"); - goto err1; - } - for (i = 0; i < 2 * k; ++i) { - if (!obj1.arrayGet(i, &obj2)->isNum()) { - error(errSyntaxError, -1, - "Invalid type in 'Encode' array in stitching function"); - goto err2; - } - encode[i] = obj2.getNum(); - obj2.free(); - } - obj1.free(); - - //----- pre-compute the scale factors - for (i = 0; i < k; ++i) { - if (bounds[i] == bounds[i+1]) { - // avoid a divide-by-zero -- in this situation, function i will - // never be used anyway - scale[i] = 0; - } else { - scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]); - } - } - - ok = gTrue; - return; - - err2: - obj2.free(); - err1: - obj1.free(); -} - -StitchingFunction::StitchingFunction(StitchingFunction *func) { - int i; - - memcpy((void *)this, (void *)func, sizeof(StitchingFunction)); - funcs = (Function **)gmallocn(k, sizeof(Function *)); - for (i = 0; i < k; ++i) { - funcs[i] = func->funcs[i]->copy(); - } - bounds = (double *)gmallocn(k + 1, sizeof(double)); - memcpy(bounds, func->bounds, (k + 1) * sizeof(double)); - encode = (double *)gmallocn(2 * k, sizeof(double)); - memcpy(encode, func->encode, 2 * k * sizeof(double)); - scale = (double *)gmallocn(k, sizeof(double)); - memcpy(scale, func->scale, k * sizeof(double)); - ok = gTrue; -} - -StitchingFunction::~StitchingFunction() { - int i; - - if (funcs) { - for (i = 0; i < k; ++i) { - if (funcs[i]) { - delete funcs[i]; - } - } - } - gfree(funcs); - gfree(bounds); - gfree(encode); - gfree(scale); -} - -void StitchingFunction::transform(double *in, double *out) { - double x; - int i; - - if (in[0] < domain[0][0]) { - x = domain[0][0]; - } else if (in[0] > domain[0][1]) { - x = domain[0][1]; - } else { - x = in[0]; - } - for (i = 0; i < k - 1; ++i) { - if (x < bounds[i+1]) { - break; - } - } - x = encode[2*i] + (x - bounds[i]) * scale[i]; - funcs[i]->transform(&x, out); -} - -//------------------------------------------------------------------------ -// PostScriptFunction -//------------------------------------------------------------------------ - -// This is not an enum, because we can't foreward-declare the enum -// type in Function.h -// -// NB: This must be kept in sync with psOpNames[] below. -#define psOpAbs 0 -#define psOpAdd 1 -#define psOpAnd 2 -#define psOpAtan 3 -#define psOpBitshift 4 -#define psOpCeiling 5 -#define psOpCopy 6 -#define psOpCos 7 -#define psOpCvi 8 -#define psOpCvr 9 -#define psOpDiv 10 -#define psOpDup 11 -#define psOpEq 12 -#define psOpExch 13 -#define psOpExp 14 -#define psOpFalse 15 -#define psOpFloor 16 -#define psOpGe 17 -#define psOpGt 18 -#define psOpIdiv 19 -#define psOpIndex 20 -#define psOpLe 21 -#define psOpLn 22 -#define psOpLog 23 -#define psOpLt 24 -#define psOpMod 25 -#define psOpMul 26 -#define psOpNe 27 -#define psOpNeg 28 -#define psOpNot 29 -#define psOpOr 30 -#define psOpPop 31 -#define psOpRoll 32 -#define psOpRound 33 -#define psOpSin 34 -#define psOpSqrt 35 -#define psOpSub 36 -#define psOpTrue 37 -#define psOpTruncate 38 -#define psOpXor 39 -// the push/j/jz ops are used internally (and are not listed in psOpNames[]) -#define psOpPush 40 -#define psOpJ 41 -#define psOpJz 42 - -#define nPSOps (sizeof(psOpNames) / sizeof(const char *)) - -// Note: 'if' and 'ifelse' are parsed separately. -// The rest are listed here in alphabetical order. -// -// NB: This must be kept in sync with the psOpXXX defines above. -static const char *psOpNames[] = { - "abs", - "add", - "and", - "atan", - "bitshift", - "ceiling", - "copy", - "cos", - "cvi", - "cvr", - "div", - "dup", - "eq", - "exch", - "exp", - "false", - "floor", - "ge", - "gt", - "idiv", - "index", - "le", - "ln", - "log", - "lt", - "mod", - "mul", - "ne", - "neg", - "not", - "or", - "pop", - "roll", - "round", - "sin", - "sqrt", - "sub", - "true", - "truncate", - "xor" -}; - -struct PSCode { - int op; - union { - double d; - int i; - } val; -}; - -#define psStackSize 100 - -PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { - Stream *str; - GList *tokens; - GString *tok; - double in[funcMaxInputs]; - int tokPtr, codePtr, i; - - codeString = NULL; - code = NULL; - codeSize = 0; - ok = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (!hasRange) { - error(errSyntaxError, -1, "Type 4 function is missing range"); - goto err1; - } - - //----- get the stream - if (!funcObj->isStream()) { - error(errSyntaxError, -1, "Type 4 function isn't a stream"); - goto err1; - } - str = funcObj->getStream(); - - //----- tokenize the function - codeString = new GString(); - tokens = new GList(); - str->reset(); - while ((tok = getToken(str))) { - tokens->append(tok); - } - str->close(); - - //----- parse the function - if (tokens->getLength() < 1 || - ((GString *)tokens->get(0))->cmp("{")) { - error(errSyntaxError, -1, "Expected '{{' at start of PostScript function"); - goto err2; - } - tokPtr = 1; - codePtr = 0; - if (!parseCode(tokens, &tokPtr, &codePtr)) { - goto err2; - } - codeLen = codePtr; - - //----- set up the cache - for (i = 0; i < m; ++i) { - in[i] = domain[i][0]; - cacheIn[i] = in[i] - 1; - } - transform(in, cacheOut); - - ok = gTrue; - - err2: - deleteGList(tokens, GString); - err1: - return; -} - -PostScriptFunction::PostScriptFunction(PostScriptFunction *func) { - memcpy((void *)this, (void *)func, sizeof(PostScriptFunction)); - codeString = func->codeString->copy(); - code = (PSCode *)gmallocn(codeSize, sizeof(PSCode)); - memcpy(code, func->code, codeSize * sizeof(PSCode)); -} - -PostScriptFunction::~PostScriptFunction() { - gfree(code); - if (codeString) { - delete codeString; - } -} - -void PostScriptFunction::transform(double *in, double *out) { - double stack[psStackSize]; - double x; - int sp, i; - - // check the cache - for (i = 0; i < m; ++i) { - if (in[i] != cacheIn[i]) { - break; - } - } - if (i == m) { - for (i = 0; i < n; ++i) { - out[i] = cacheOut[i]; - } - return; - } - - for (i = 0; i < m; ++i) { - stack[psStackSize - 1 - i] = in[i]; - } - sp = exec(stack, psStackSize - m); - // if (sp < psStackSize - n) { - // error(errSyntaxWarning, -1, - // "Extra values on stack at end of PostScript function"); - // } - if (sp > psStackSize - n) { - error(errSyntaxError, -1, "Stack underflow in PostScript function"); - sp = psStackSize - n; - } - for (i = 0; i < n; ++i) { - x = stack[sp + n - 1 - i]; - if (x < range[i][0]) { - out[i] = range[i][0]; - } else if (x > range[i][1]) { - out[i] = range[i][1]; - } else { - out[i] = x; - } - } - - // save current result in the cache - for (i = 0; i < m; ++i) { - cacheIn[i] = in[i]; - } - for (i = 0; i < n; ++i) { - cacheOut[i] = out[i]; - } -} - -GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) { - GString *tok; - char *p; - int a, b, mid, cmp; - int codePtr0, codePtr1; - - while (1) { - if (*tokPtr >= tokens->getLength()) { - error(errSyntaxError, -1, - "Unexpected end of PostScript function stream"); - return gFalse; - } - tok = (GString *)tokens->get((*tokPtr)++); - p = tok->getCString(); - if (isdigit(*p) || *p == '.' || *p == '-') { - addCodeD(codePtr, psOpPush, atof(tok->getCString())); - } else if (!tok->cmp("{")) { - codePtr0 = *codePtr; - addCodeI(codePtr, psOpJz, 0); - if (!parseCode(tokens, tokPtr, codePtr)) { - return gFalse; - } - if (*tokPtr >= tokens->getLength()) { - error(errSyntaxError, -1, - "Unexpected end of PostScript function stream"); - return gFalse; - } - tok = (GString *)tokens->get((*tokPtr)++); - if (!tok->cmp("if")) { - code[codePtr0].val.i = *codePtr; - } else if (!tok->cmp("{")) { - codePtr1 = *codePtr; - addCodeI(codePtr, psOpJ, 0); - code[codePtr0].val.i = *codePtr; - if (!parseCode(tokens, tokPtr, codePtr)) { - return gFalse; - } - if (*tokPtr >= tokens->getLength()) { - error(errSyntaxError, -1, - "Unexpected end of PostScript function stream"); - return gFalse; - } - tok = (GString *)tokens->get((*tokPtr)++); - if (!tok->cmp("ifelse")) { - code[codePtr1].val.i = *codePtr; - } else { - error(errSyntaxError, -1, - "Expected 'ifelse' in PostScript function stream"); - return gFalse; - } - } else { - error(errSyntaxError, -1, - "Expected 'if' in PostScript function stream"); - return gFalse; - } - } else if (!tok->cmp("}")) { - break; - } else if (!tok->cmp("if")) { - error(errSyntaxError, -1, - "Unexpected 'if' in PostScript function stream"); - return gFalse; - } else if (!tok->cmp("ifelse")) { - error(errSyntaxError, -1, - "Unexpected 'ifelse' in PostScript function stream"); - return gFalse; - } else { - a = -1; - b = nPSOps; - cmp = 0; // make gcc happy - // invariant: psOpNames[a] < tok < psOpNames[b] - while (b - a > 1) { - mid = (a + b) / 2; - cmp = tok->cmp(psOpNames[mid]); - if (cmp > 0) { - a = mid; - } else if (cmp < 0) { - b = mid; - } else { - a = b = mid; - } - } - if (cmp != 0) { - error(errSyntaxError, -1, - "Unknown operator '{0:t}' in PostScript function", - tok); - return gFalse; - } - addCode(codePtr, a); - } - } - return gTrue; -} - -void PostScriptFunction::addCode(int *codePtr, int op) { - if (*codePtr >= codeSize) { - if (codeSize) { - codeSize *= 2; - } else { - codeSize = 16; - } - code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); - } - code[*codePtr].op = op; - ++(*codePtr); -} - -void PostScriptFunction::addCodeI(int *codePtr, int op, int x) { - if (*codePtr >= codeSize) { - if (codeSize) { - codeSize *= 2; - } else { - codeSize = 16; - } - code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); - } - code[*codePtr].op = op; - code[*codePtr].val.i = x; - ++(*codePtr); -} - -void PostScriptFunction::addCodeD(int *codePtr, int op, double x) { - if (*codePtr >= codeSize) { - if (codeSize) { - codeSize *= 2; - } else { - codeSize = 16; - } - code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); - } - code[*codePtr].op = op; - code[*codePtr].val.d = x; - ++(*codePtr); -} - -GString *PostScriptFunction::getToken(Stream *str) { - GString *s; - int c; - GBool comment; - - s = new GString(); - comment = gFalse; - while (1) { - if ((c = str->getChar()) == EOF) { - delete s; - return NULL; - } - codeString->append((char)c); - if (comment) { - if (c == '\x0a' || c == '\x0d') { - comment = gFalse; - } - } else if (c == '%') { - comment = gTrue; - } else if (!isspace(c)) { - break; - } - } - if (c == '{' || c == '}') { - s->append((char)c); - } else if (isdigit(c) || c == '.' || c == '-') { - while (1) { - s->append((char)c); - c = str->lookChar(); - if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { - break; - } - str->getChar(); - codeString->append((char)c); - } - } else { - while (1) { - s->append((char)c); - c = str->lookChar(); - if (c == EOF || !isalnum(c)) { - break; - } - str->getChar(); - codeString->append((char)c); - } - } - return s; -} - -int PostScriptFunction::exec(double *stack, int sp0) { - PSCode *c; - double tmp[psStackSize]; - double t; - int sp, ip, nn, k, i; - - sp = sp0; - ip = 0; - while (ip < codeLen) { - c = &code[ip++]; - switch(c->op) { - case psOpAbs: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = fabs(stack[sp]); - break; - case psOpAdd: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] + stack[sp]; - ++sp; - break; - case psOpAnd: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp]; - ++sp; - break; - case psOpAtan: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = atan2(stack[sp + 1], stack[sp]); - ++sp; - break; - case psOpBitshift: - if (sp + 1 >= psStackSize) { - goto underflow; - } - k = (int)stack[sp + 1]; - nn = (int)stack[sp]; - if (nn > 0) { - stack[sp + 1] = k << nn; - } else if (nn < 0) { - stack[sp + 1] = k >> -nn; - } else { - stack[sp + 1] = k; - } - ++sp; - break; - case psOpCeiling: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = ceil(stack[sp]); - break; - case psOpCopy: - if (sp >= psStackSize) { - goto underflow; - } - nn = (int)stack[sp++]; - if (nn < 0) { - goto invalidArg; - } - if (sp + nn > psStackSize) { - goto underflow; - } - if (sp - nn < 0) { - goto overflow; - } - for (i = 0; i < nn; ++i) { - stack[sp - nn + i] = stack[sp + i]; - } - sp -= nn; - break; - case psOpCos: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = cos(stack[sp]); - break; - case psOpCvi: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = (int)stack[sp]; - break; - case psOpCvr: - if (sp >= psStackSize) { - goto underflow; - } - break; - case psOpDiv: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] / stack[sp]; - ++sp; - break; - case psOpDup: - if (sp >= psStackSize) { - goto underflow; - } - if (sp < 1) { - goto overflow; - } - stack[sp - 1] = stack[sp]; - --sp; - break; - case psOpEq: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0; - ++sp; - break; - case psOpExch: - if (sp + 1 >= psStackSize) { - goto underflow; - } - t = stack[sp]; - stack[sp] = stack[sp + 1]; - stack[sp + 1] = t; - break; - case psOpExp: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = pow(stack[sp + 1], stack[sp]); - ++sp; - break; - case psOpFalse: - if (sp < 1) { - goto overflow; - } - stack[sp - 1] = 0; - --sp; - break; - case psOpFloor: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = floor(stack[sp]); - break; - case psOpGe: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0; - ++sp; - break; - case psOpGt: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0; - ++sp; - break; - case psOpIdiv: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp]; - ++sp; - break; - case psOpIndex: - if (sp >= psStackSize) { - goto underflow; - } - k = (int)stack[sp]; - if (k < 0) { - goto invalidArg; - } - if (sp + 1 + k >= psStackSize) { - goto underflow; - } - stack[sp] = stack[sp + 1 + k]; - break; - case psOpLe: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0; - ++sp; - break; - case psOpLn: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = log(stack[sp]); - break; - case psOpLog: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = log10(stack[sp]); - break; - case psOpLt: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0; - ++sp; - break; - case psOpMod: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp]; - ++sp; - break; - case psOpMul: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] * stack[sp]; - ++sp; - break; - case psOpNe: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0; - ++sp; - break; - case psOpNeg: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = -stack[sp]; - break; - case psOpNot: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = stack[sp] == 0 ? 1 : 0; - break; - case psOpOr: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp]; - ++sp; - break; - case psOpPop: - if (sp >= psStackSize) { - goto underflow; - } - ++sp; - break; - case psOpRoll: - if (sp + 1 >= psStackSize) { - goto underflow; - } - k = (int)stack[sp++]; - nn = (int)stack[sp++]; - if (nn < 0) { - goto invalidArg; - } - if (sp + nn > psStackSize) { - goto underflow; - } - if (k >= 0) { - k %= nn; - } else { - k = -k % nn; - if (k) { - k = nn - k; - } - } - for (i = 0; i < nn; ++i) { - tmp[i] = stack[sp + i]; - } - for (i = 0; i < nn; ++i) { - stack[sp + i] = tmp[(i + k) % nn]; - } - break; - case psOpRound: - if (sp >= psStackSize) { - goto underflow; - } - t = stack[sp]; - stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5); - break; - case psOpSin: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = sin(stack[sp]); - break; - case psOpSqrt: - if (sp >= psStackSize) { - goto underflow; - } - stack[sp] = sqrt(stack[sp]); - break; - case psOpSub: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = stack[sp + 1] - stack[sp]; - ++sp; - break; - case psOpTrue: - if (sp < 1) { - goto overflow; - } - stack[sp - 1] = 1; - --sp; - break; - case psOpTruncate: - if (sp >= psStackSize) { - goto underflow; - } - t = stack[sp]; - stack[sp] = (t >= 0) ? floor(t) : ceil(t); - break; - case psOpXor: - if (sp + 1 >= psStackSize) { - goto underflow; - } - stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp]; - ++sp; - break; - case psOpPush: - if (sp < 1) { - goto overflow; - } - stack[--sp] = c->val.d; - break; - case psOpJ: - ip = c->val.i; - break; - case psOpJz: - if (sp >= psStackSize) { - goto underflow; - } - k = (int)stack[sp++]; - if (k == 0) { - ip = c->val.i; - } - break; - } - } - return sp; - - underflow: - error(errSyntaxError, -1, "Stack underflow in PostScript function"); - return sp; - overflow: - error(errSyntaxError, -1, "Stack overflow in PostScript function"); - return sp; - invalidArg: - error(errSyntaxError, -1, "Invalid arg in PostScript function"); - return sp; -} diff --git a/test/bug-hunting/cve/CVE-2019-10026/Function.h b/test/bug-hunting/cve/CVE-2019-10026/Function.h deleted file mode 100644 index 615c2abfddf..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10026/Function.h +++ /dev/null @@ -1,310 +0,0 @@ -//======================================================================== -// -// Function.h -// -// Copyright 2001-2003 Glyph & Cog, LLC -// -//======================================================================== - -#ifndef FUNCTION_H -#define FUNCTION_H - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "gtypes.h" -#include "Object.h" - -class GList; -class Dict; -class Stream; -struct PSCode; - -//------------------------------------------------------------------------ -// Function -//------------------------------------------------------------------------ - -#define funcMaxInputs 32 -#define funcMaxOutputs 32 -#define sampledFuncMaxInputs 16 - -class Function { -public: - - Function(); - - virtual ~Function(); - - // Construct a function. Returns NULL if unsuccessful. - static Function *parse(Object *funcObj, int recursion = 0); - - // Initialize the entries common to all function types. - GBool init(Dict *dict); - - virtual Function *copy() = 0; - - // Return the function type: - // -1 : identity - // 0 : sampled - // 2 : exponential - // 3 : stitching - // 4 : PostScript - virtual int getType() = 0; - - // Return size of input and output tuples. - int getInputSize() { - return m; - } - int getOutputSize() { - return n; - } - - double getDomainMin(int i) { - return domain[i][0]; - } - double getDomainMax(int i) { - return domain[i][1]; - } - double getRangeMin(int i) { - return range[i][0]; - } - double getRangeMax(int i) { - return range[i][1]; - } - GBool getHasRange() { - return hasRange; - } - - // Transform an input tuple into an output tuple. - virtual void transform(double *in, double *out) = 0; - - virtual GBool isOk() = 0; - -protected: - - int m, n; // size of input and output tuples - double // min and max values for function domain - domain[funcMaxInputs][2]; - double // min and max values for function range - range[funcMaxOutputs][2]; - GBool hasRange; // set if range is defined -}; - -//------------------------------------------------------------------------ -// IdentityFunction -//------------------------------------------------------------------------ - -class IdentityFunction : public Function { -public: - - IdentityFunction(); - virtual ~IdentityFunction(); - virtual Function *copy() { - return new IdentityFunction(); - } - virtual int getType() { - return -1; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return gTrue; - } - -private: -}; - -//------------------------------------------------------------------------ -// SampledFunction -//------------------------------------------------------------------------ - -class SampledFunction : public Function { -public: - - SampledFunction(Object *funcObj, Dict *dict); - virtual ~SampledFunction(); - virtual Function *copy() { - return new SampledFunction(this); - } - virtual int getType() { - return 0; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - int getSampleSize(int i) { - return sampleSize[i]; - } - double getEncodeMin(int i) { - return encode[i][0]; - } - double getEncodeMax(int i) { - return encode[i][1]; - } - double getDecodeMin(int i) { - return decode[i][0]; - } - double getDecodeMax(int i) { - return decode[i][1]; - } - double *getSamples() { - return samples; - } - -private: - - SampledFunction(SampledFunction *func); - - int // number of samples for each domain element - sampleSize[funcMaxInputs]; - double // min and max values for domain encoder - encode[funcMaxInputs][2]; - double // min and max values for range decoder - decode[funcMaxOutputs][2]; - double // input multipliers - inputMul[funcMaxInputs]; - int *idxOffset; - double *samples; // the samples - int nSamples; // size of the samples array - double *sBuf; // buffer for the transform function - double cacheIn[funcMaxInputs]; - double cacheOut[funcMaxOutputs]; - GBool ok; -}; - -//------------------------------------------------------------------------ -// ExponentialFunction -//------------------------------------------------------------------------ - -class ExponentialFunction : public Function { -public: - - ExponentialFunction(Object *funcObj, Dict *dict); - virtual ~ExponentialFunction(); - virtual Function *copy() { - return new ExponentialFunction(this); - } - virtual int getType() { - return 2; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - double *getC0() { - return c0; - } - double *getC1() { - return c1; - } - double getE() { - return e; - } - -private: - - ExponentialFunction(ExponentialFunction *func); - - double c0[funcMaxOutputs]; - double c1[funcMaxOutputs]; - double e; - GBool ok; -}; - -//------------------------------------------------------------------------ -// StitchingFunction -//------------------------------------------------------------------------ - -class StitchingFunction : public Function { -public: - - StitchingFunction(Object *funcObj, Dict *dict, int recursion); - virtual ~StitchingFunction(); - virtual Function *copy() { - return new StitchingFunction(this); - } - virtual int getType() { - return 3; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - int getNumFuncs() { - return k; - } - Function *getFunc(int i) { - return funcs[i]; - } - double *getBounds() { - return bounds; - } - double *getEncode() { - return encode; - } - double *getScale() { - return scale; - } - -private: - - StitchingFunction(StitchingFunction *func); - - int k; - Function **funcs; - double *bounds; - double *encode; - double *scale; - GBool ok; -}; - -//------------------------------------------------------------------------ -// PostScriptFunction -//------------------------------------------------------------------------ - -class PostScriptFunction : public Function { -public: - - PostScriptFunction(Object *funcObj, Dict *dict); - virtual ~PostScriptFunction(); - virtual Function *copy() { - return new PostScriptFunction(this); - } - virtual int getType() { - return 4; - } - virtual void transform(double *in, double *out); - virtual GBool isOk() { - return ok; - } - - GString *getCodeString() { - return codeString; - } - -private: - - PostScriptFunction(PostScriptFunction *func); - GBool parseCode(GList *tokens, int *tokPtr, int *codePtr); - void addCode(int *codePtr, int op); - void addCodeI(int *codePtr, int op, int x); - void addCodeD(int *codePtr, int op, double x); - GString *getToken(Stream *str); - int exec(double *stack, int sp0); - - GString *codeString; - PSCode *code; - int codeLen; - int codeSize; - double cacheIn[funcMaxInputs]; - double cacheOut[funcMaxOutputs]; - GBool ok; -}; - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-10026/README b/test/bug-hunting/cve/CVE-2019-10026/README deleted file mode 100644 index 9ce551f5ef9..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10026/README +++ /dev/null @@ -1,3 +0,0 @@ -Details: -https://nvd.nist.gov/vuln/detail/CVE-2019-10026 - diff --git a/test/bug-hunting/cve/CVE-2019-10026/expected.txt b/test/bug-hunting/cve/CVE-2019-10026/expected.txt deleted file mode 100644 index 20c3b25d672..00000000000 --- a/test/bug-hunting/cve/CVE-2019-10026/expected.txt +++ /dev/null @@ -1,4 +0,0 @@ -Function.cc:1475:bughuntingDivByZero -Function.cc:1477:bughuntingDivByZero -Function.cc:1486:bughuntingDivByZero - diff --git a/test/bug-hunting/cve/CVE-2019-1010315/dsdiff.c b/test/bug-hunting/cve/CVE-2019-1010315/dsdiff.c deleted file mode 100644 index 2fe6aa6e533..00000000000 --- a/test/bug-hunting/cve/CVE-2019-1010315/dsdiff.c +++ /dev/null @@ -1,443 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// **** WAVPACK **** // -// Hybrid Lossless Wavefile Compressor // -// Copyright (c) 1998 - 2019 David Bryant. // -// All Rights Reserved. // -// Distributed under the BSD Software License (see license.txt) // -//////////////////////////////////////////////////////////////////////////// - -// dsdiff.c - -// This module is a helper to the WavPack command-line programs to support DFF files. - -#include -#include -#include -#include -#include -#include - -#include "wavpack.h" -#include "utils.h" -#include "md5.h" - -#ifdef _WIN32 -#define strdup(x) _strdup(x) -#endif - -#define WAVPACK_NO_ERROR 0 -#define WAVPACK_SOFT_ERROR 1 -#define WAVPACK_HARD_ERROR 2 - -extern int debug_logging_mode; - -#pragma pack(push,2) - -typedef struct { - char ckID[4]; - int64_t ckDataSize; -} DFFChunkHeader; - -typedef struct { - char ckID[4]; - int64_t ckDataSize; - char formType[4]; -} DFFFileHeader; - -typedef struct { - char ckID[4]; - int64_t ckDataSize; - uint32_t version; -} DFFVersionChunk; - -typedef struct { - char ckID[4]; - int64_t ckDataSize; - uint32_t sampleRate; -} DFFSampleRateChunk; - -typedef struct { - char ckID[4]; - int64_t ckDataSize; - uint16_t numChannels; -} DFFChannelsHeader; - -typedef struct { - char ckID[4]; - int64_t ckDataSize; - char compressionType[4]; -} DFFCompressionHeader; - -#pragma pack(pop) - -#define DFFChunkHeaderFormat "4D" -#define DFFFileHeaderFormat "4D4" -#define DFFVersionChunkFormat "4DL" -#define DFFSampleRateChunkFormat "4DL" -#define DFFChannelsHeaderFormat "4DS" -#define DFFCompressionHeaderFormat "4D4" - -int ParseDsdiffHeaderConfig (FILE *infile, char *infilename, char *fourcc, WavpackContext *wpc, WavpackConfig *config) -{ - int64_t infilesize, total_samples; - DFFFileHeader dff_file_header; - DFFChunkHeader dff_chunk_header; - uint32_t bcount; - - infilesize = DoGetFileSize (infile); - memcpy (&dff_file_header, fourcc, 4); - - if ((!DoReadFile (infile, ((char *) &dff_file_header) + 4, sizeof (DFFFileHeader) - 4, &bcount) || - bcount != sizeof (DFFFileHeader) - 4) || strncmp (dff_file_header.formType, "DSD ", 4)) { - error_line ("%s is not a valid .DFF file!", infilename); - return WAVPACK_SOFT_ERROR; - } - else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) && - !WavpackAddWrapper (wpc, &dff_file_header, sizeof (DFFFileHeader))) { - error_line ("%s", WavpackGetErrorMessage (wpc)); - return WAVPACK_SOFT_ERROR; - } - -#if 1 // this might be a little too picky... - WavpackBigEndianToNative (&dff_file_header, DFFFileHeaderFormat); - - if (infilesize && !(config->qmode & QMODE_IGNORE_LENGTH) && - dff_file_header.ckDataSize && dff_file_header.ckDataSize + 1 && dff_file_header.ckDataSize + 12 != infilesize) { - error_line ("%s is not a valid .DFF file (by total size)!", infilename); - return WAVPACK_SOFT_ERROR; - } - - if (debug_logging_mode) - error_line ("file header indicated length = %lld", dff_file_header.ckDataSize); - -#endif - - // loop through all elements of the DSDIFF header - // (until the data chuck) and copy them to the output file - - while (1) { - if (!DoReadFile (infile, &dff_chunk_header, sizeof (DFFChunkHeader), &bcount) || - bcount != sizeof (DFFChunkHeader)) { - error_line ("%s is not a valid .DFF file!", infilename); - return WAVPACK_SOFT_ERROR; - } - else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) && - !WavpackAddWrapper (wpc, &dff_chunk_header, sizeof (DFFChunkHeader))) { - error_line ("%s", WavpackGetErrorMessage (wpc)); - return WAVPACK_SOFT_ERROR; - } - - WavpackBigEndianToNative (&dff_chunk_header, DFFChunkHeaderFormat); - - if (debug_logging_mode) - error_line ("chunk header indicated length = %lld", dff_chunk_header.ckDataSize); - - if (!strncmp (dff_chunk_header.ckID, "FVER", 4)) { - uint32_t version; - - if (dff_chunk_header.ckDataSize != sizeof (version) || - !DoReadFile (infile, &version, sizeof (version), &bcount) || - bcount != sizeof (version)) { - error_line ("%s is not a valid .DFF file!", infilename); - return WAVPACK_SOFT_ERROR; - } - else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) && - !WavpackAddWrapper (wpc, &version, sizeof (version))) { - error_line ("%s", WavpackGetErrorMessage (wpc)); - return WAVPACK_SOFT_ERROR; - } - - WavpackBigEndianToNative (&version, "L"); - - if (debug_logging_mode) - error_line ("dsdiff file version = 0x%08x", version); - } - else if (!strncmp (dff_chunk_header.ckID, "PROP", 4)) { - char *prop_chunk; - - if (dff_chunk_header.ckDataSize < 4 || dff_chunk_header.ckDataSize > 1024) { - error_line ("%s is not a valid .DFF file!", infilename); - return WAVPACK_SOFT_ERROR; - } - - if (debug_logging_mode) - error_line ("got PROP chunk of %d bytes total", (int) dff_chunk_header.ckDataSize); - - prop_chunk = malloc ((size_t) dff_chunk_header.ckDataSize); - - if (!DoReadFile (infile, prop_chunk, (uint32_t) dff_chunk_header.ckDataSize, &bcount) || - bcount != dff_chunk_header.ckDataSize) { - error_line ("%s is not a valid .DFF file!", infilename); - free (prop_chunk); - return WAVPACK_SOFT_ERROR; - } - else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) && - !WavpackAddWrapper (wpc, prop_chunk, (uint32_t) dff_chunk_header.ckDataSize)) { - error_line ("%s", WavpackGetErrorMessage (wpc)); - free (prop_chunk); - return WAVPACK_SOFT_ERROR; - } - - if (!strncmp (prop_chunk, "SND ", 4)) { - char *cptr = prop_chunk + 4, *eptr = prop_chunk + dff_chunk_header.ckDataSize; - uint16_t numChannels, chansSpecified, chanMask = 0; - uint32_t sampleRate; - - while (eptr - cptr >= sizeof (dff_chunk_header)) { - memcpy (&dff_chunk_header, cptr, sizeof (dff_chunk_header)); - cptr += sizeof (dff_chunk_header); - WavpackBigEndianToNative (&dff_chunk_header, DFFChunkHeaderFormat); - - if (dff_chunk_header.ckDataSize > 0 && dff_chunk_header.ckDataSize <= eptr - cptr) { - if (!strncmp (dff_chunk_header.ckID, "FS ", 4) && dff_chunk_header.ckDataSize == 4) { - memcpy (&sampleRate, cptr, sizeof (sampleRate)); - WavpackBigEndianToNative (&sampleRate, "L"); - cptr += dff_chunk_header.ckDataSize; - - if (debug_logging_mode) - error_line ("got sample rate of %u Hz", sampleRate); - } - else if (!strncmp (dff_chunk_header.ckID, "CHNL", 4) && dff_chunk_header.ckDataSize >= 2) { - memcpy (&numChannels, cptr, sizeof (numChannels)); - WavpackBigEndianToNative (&numChannels, "S"); - cptr += sizeof (numChannels); - - chansSpecified = (int)(dff_chunk_header.ckDataSize - sizeof (numChannels)) / 4; - - if (numChannels < chansSpecified || numChannels < 1) { - error_line ("%s is not a valid .DFF file!", infilename); - free (prop_chunk); - return WAVPACK_SOFT_ERROR; - } - - while (chansSpecified--) { - if (!strncmp (cptr, "SLFT", 4) || !strncmp (cptr, "MLFT", 4)) - chanMask |= 0x1; - else if (!strncmp (cptr, "SRGT", 4) || !strncmp (cptr, "MRGT", 4)) - chanMask |= 0x2; - else if (!strncmp (cptr, "LS ", 4)) - chanMask |= 0x10; - else if (!strncmp (cptr, "RS ", 4)) - chanMask |= 0x20; - else if (!strncmp (cptr, "C ", 4)) - chanMask |= 0x4; - else if (!strncmp (cptr, "LFE ", 4)) - chanMask |= 0x8; - else - if (debug_logging_mode) - error_line ("undefined channel ID %c%c%c%c", cptr [0], cptr [1], cptr [2], cptr [3]); - - cptr += 4; - } - - if (debug_logging_mode) - error_line ("%d channels, mask = 0x%08x", numChannels, chanMask); - } - else if (!strncmp (dff_chunk_header.ckID, "CMPR", 4) && dff_chunk_header.ckDataSize >= 4) { - if (strncmp (cptr, "DSD ", 4)) { - error_line ("DSDIFF files must be uncompressed, not \"%c%c%c%c\"!", - cptr [0], cptr [1], cptr [2], cptr [3]); - free (prop_chunk); - return WAVPACK_SOFT_ERROR; - } - - cptr += dff_chunk_header.ckDataSize; - } - else { - if (debug_logging_mode) - error_line ("got PROP/SND chunk type \"%c%c%c%c\" of %d bytes", dff_chunk_header.ckID [0], - dff_chunk_header.ckID [1], dff_chunk_header.ckID [2], dff_chunk_header.ckID [3], dff_chunk_header.ckDataSize); - - cptr += dff_chunk_header.ckDataSize; - } - } - else { - error_line ("%s is not a valid .DFF file!", infilename); - free (prop_chunk); - return WAVPACK_SOFT_ERROR; - } - } - - if (chanMask && (config->channel_mask || (config->qmode & QMODE_CHANS_UNASSIGNED))) { - error_line ("this DSDIFF file already has channel order information!"); - free (prop_chunk); - return WAVPACK_SOFT_ERROR; - } - else if (chanMask) - config->channel_mask = chanMask; - - config->bits_per_sample = 8; - config->bytes_per_sample = 1; - config->num_channels = numChannels; - config->sample_rate = sampleRate / 8; - config->qmode |= QMODE_DSD_MSB_FIRST; - } - else if (debug_logging_mode) - error_line ("got unknown PROP chunk type \"%c%c%c%c\" of %d bytes", - prop_chunk [0], prop_chunk [1], prop_chunk [2], prop_chunk [3], dff_chunk_header.ckDataSize); - - free (prop_chunk); - } - else if (!strncmp (dff_chunk_header.ckID, "DSD ", 4)) { - total_samples = dff_chunk_header.ckDataSize / config->num_channels; - break; - } - else { // just copy unknown chunks to output file - - int bytes_to_copy = (int)(((dff_chunk_header.ckDataSize) + 1) & ~(int64_t)1); - char *buff; - - if (bytes_to_copy < 0 || bytes_to_copy > 4194304) { - error_line ("%s is not a valid .DFF file!", infilename); - return WAVPACK_SOFT_ERROR; - } - - buff = malloc (bytes_to_copy); - - if (debug_logging_mode) - error_line ("extra unknown chunk \"%c%c%c%c\" of %d bytes", - dff_chunk_header.ckID [0], dff_chunk_header.ckID [1], dff_chunk_header.ckID [2], - dff_chunk_header.ckID [3], dff_chunk_header.ckDataSize); - - if (!DoReadFile (infile, buff, bytes_to_copy, &bcount) || - bcount != bytes_to_copy || - (!(config->qmode & QMODE_NO_STORE_WRAPPER) && - !WavpackAddWrapper (wpc, buff, bytes_to_copy))) { - error_line ("%s", WavpackGetErrorMessage (wpc)); - free (buff); - return WAVPACK_SOFT_ERROR; - } - - free (buff); - } - } - - if (debug_logging_mode) - error_line ("setting configuration with %lld samples", total_samples); - - if (!WavpackSetConfiguration64 (wpc, config, total_samples, NULL)) { - error_line ("%s: %s", infilename, WavpackGetErrorMessage (wpc)); - return WAVPACK_SOFT_ERROR; - } - - return WAVPACK_NO_ERROR; -} - -int WriteDsdiffHeader (FILE *outfile, WavpackContext *wpc, int64_t total_samples, int qmode) -{ - uint32_t chan_mask = WavpackGetChannelMask (wpc); - int num_channels = WavpackGetNumChannels (wpc); - DFFFileHeader file_header, prop_header; - DFFChunkHeader data_header; - DFFVersionChunk ver_chunk; - DFFSampleRateChunk fs_chunk; - DFFChannelsHeader chan_header; - DFFCompressionHeader cmpr_header; - char *cmpr_name = "\016not compressed", *chan_ids; - int64_t file_size, prop_chunk_size, data_size; - int cmpr_name_size, chan_ids_size; - uint32_t bcount; - - if (debug_logging_mode) - error_line ("WriteDsdiffHeader (), total samples = %lld, qmode = 0x%02x\n", - (long long) total_samples, qmode); - - cmpr_name_size = (strlen (cmpr_name) + 1) & ~1; - chan_ids_size = num_channels * 4; - chan_ids = malloc (chan_ids_size); - - if (chan_ids) { - uint32_t scan_mask = 0x1; - char *cptr = chan_ids; - int ci, uci = 0; - - for (ci = 0; ci < num_channels; ++ci) { - while (scan_mask && !(scan_mask & chan_mask)) - scan_mask <<= 1; - - if (scan_mask & 0x1) - memcpy (cptr, num_channels <= 2 ? "SLFT" : "MLFT", 4); - else if (scan_mask & 0x2) - memcpy (cptr, num_channels <= 2 ? "SRGT" : "MRGT", 4); - else if (scan_mask & 0x4) - memcpy (cptr, "C ", 4); - else if (scan_mask & 0x8) - memcpy (cptr, "LFE ", 4); - else if (scan_mask & 0x10) - memcpy (cptr, "LS ", 4); - else if (scan_mask & 0x20) - memcpy (cptr, "RS ", 4); - else { - cptr [0] = 'C'; - cptr [1] = (uci / 100) + '0'; - cptr [2] = ((uci % 100) / 10) + '0'; - cptr [3] = (uci % 10) + '0'; - uci++; - } - - scan_mask <<= 1; - cptr += 4; - } - } - else { - error_line ("can't allocate memory!"); - return FALSE; - } - - data_size = total_samples * num_channels; - prop_chunk_size = sizeof (prop_header) + sizeof (fs_chunk) + sizeof (chan_header) + chan_ids_size + sizeof (cmpr_header) + cmpr_name_size; - file_size = sizeof (file_header) + sizeof (ver_chunk) + prop_chunk_size + sizeof (data_header) + ((data_size + 1) & ~(int64_t)1); - - memcpy (file_header.ckID, "FRM8", 4); - file_header.ckDataSize = file_size - 12; - memcpy (file_header.formType, "DSD ", 4); - - memcpy (prop_header.ckID, "PROP", 4); - prop_header.ckDataSize = prop_chunk_size - 12; - memcpy (prop_header.formType, "SND ", 4); - - memcpy (ver_chunk.ckID, "FVER", 4); - ver_chunk.ckDataSize = sizeof (ver_chunk) - 12; - ver_chunk.version = 0x01050000; - - memcpy (fs_chunk.ckID, "FS ", 4); - fs_chunk.ckDataSize = sizeof (fs_chunk) - 12; - fs_chunk.sampleRate = WavpackGetSampleRate (wpc) * 8; - - memcpy (chan_header.ckID, "CHNL", 4); - chan_header.ckDataSize = sizeof (chan_header) + chan_ids_size - 12; - chan_header.numChannels = num_channels; - - memcpy (cmpr_header.ckID, "CMPR", 4); - cmpr_header.ckDataSize = sizeof (cmpr_header) + cmpr_name_size - 12; - memcpy (cmpr_header.compressionType, "DSD ", 4); - - memcpy (data_header.ckID, "DSD ", 4); - data_header.ckDataSize = data_size; - - WavpackNativeToBigEndian (&file_header, DFFFileHeaderFormat); - WavpackNativeToBigEndian (&ver_chunk, DFFVersionChunkFormat); - WavpackNativeToBigEndian (&prop_header, DFFFileHeaderFormat); - WavpackNativeToBigEndian (&fs_chunk, DFFSampleRateChunkFormat); - WavpackNativeToBigEndian (&chan_header, DFFChannelsHeaderFormat); - WavpackNativeToBigEndian (&cmpr_header, DFFCompressionHeaderFormat); - WavpackNativeToBigEndian (&data_header, DFFChunkHeaderFormat); - - if (!DoWriteFile (outfile, &file_header, sizeof (file_header), &bcount) || bcount != sizeof (file_header) || - !DoWriteFile (outfile, &ver_chunk, sizeof (ver_chunk), &bcount) || bcount != sizeof (ver_chunk) || - !DoWriteFile (outfile, &prop_header, sizeof (prop_header), &bcount) || bcount != sizeof (prop_header) || - !DoWriteFile (outfile, &fs_chunk, sizeof (fs_chunk), &bcount) || bcount != sizeof (fs_chunk) || - !DoWriteFile (outfile, &chan_header, sizeof (chan_header), &bcount) || bcount != sizeof (chan_header) || - !DoWriteFile (outfile, chan_ids, chan_ids_size, &bcount) || bcount != chan_ids_size || - !DoWriteFile (outfile, &cmpr_header, sizeof (cmpr_header), &bcount) || bcount != sizeof (cmpr_header) || - !DoWriteFile (outfile, cmpr_name, cmpr_name_size, &bcount) || bcount != cmpr_name_size || - !DoWriteFile (outfile, &data_header, sizeof (data_header), &bcount) || bcount != sizeof (data_header)) { - error_line ("can't write .DSF data, disk probably full!"); - free (chan_ids); - return FALSE; - } - - free (chan_ids); - return TRUE; -} - diff --git a/test/bug-hunting/cve/CVE-2019-1010315/expected.txt b/test/bug-hunting/cve/CVE-2019-1010315/expected.txt deleted file mode 100644 index b7e906ff1bc..00000000000 --- a/test/bug-hunting/cve/CVE-2019-1010315/expected.txt +++ /dev/null @@ -1 +0,0 @@ -dsdiff.c:282:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-12977/cmd.txt b/test/bug-hunting/cve/CVE-2019-12977/cmd.txt deleted file mode 100644 index 7000b0c5f1d..00000000000 --- a/test/bug-hunting/cve/CVE-2019-12977/cmd.txt +++ /dev/null @@ -1,2 +0,0 @@ --DMAGICKCORE_LIBOPENJP2_DELEGATE - diff --git a/test/bug-hunting/cve/CVE-2019-12977/expected.txt b/test/bug-hunting/cve/CVE-2019-12977/expected.txt deleted file mode 100644 index 4e0e618ff33..00000000000 --- a/test/bug-hunting/cve/CVE-2019-12977/expected.txt +++ /dev/null @@ -1 +0,0 @@ -jp2.c:865:bughuntingUninit diff --git a/test/bug-hunting/cve/CVE-2019-12977/jp2.c b/test/bug-hunting/cve/CVE-2019-12977/jp2.c deleted file mode 100644 index 911f705b1c2..00000000000 --- a/test/bug-hunting/cve/CVE-2019-12977/jp2.c +++ /dev/null @@ -1,1106 +0,0 @@ -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % JJJ PPPP 222 % - % J P P 2 2 % - % J PPPP 22 % - % J J P 2 % - % JJ P 22222 % - % % - % % - % Read/Write JPEG-2000 Image Format % - % % - % Cristy % - % Nathan Brown % - % June 2001 % - % % - % % - % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization % - % dedicated to making software imaging solutions freely available. % - % % - % You may not use this file except in compliance with the License. You may % - % obtain a copy of the License at % - % % - % https://imagemagick.org/script/license.php % - % % - % Unless required by applicable law or agreed to in writing, software % - % distributed under the License is distributed on an "AS IS" BASIS, % - % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % - % See the License for the specific language governing permissions and % - % limitations under the License. % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % - */ - -/* - Include declarations. - */ -#include "MagickCore/studio.h" -#include "MagickCore/artifact.h" -#include "MagickCore/attribute.h" -#include "MagickCore/blob.h" -#include "MagickCore/blob-private.h" -#include "MagickCore/cache.h" -#include "MagickCore/colorspace.h" -#include "MagickCore/colorspace-private.h" -#include "MagickCore/color.h" -#include "MagickCore/color-private.h" -#include "MagickCore/exception.h" -#include "MagickCore/exception-private.h" -#include "MagickCore/image.h" -#include "MagickCore/image-private.h" -#include "MagickCore/list.h" -#include "MagickCore/magick.h" -#include "MagickCore/memory_.h" -#include "MagickCore/monitor.h" -#include "MagickCore/monitor-private.h" -#include "MagickCore/option.h" -#include "MagickCore/pixel-accessor.h" -#include "MagickCore/profile.h" -#include "MagickCore/property.h" -#include "MagickCore/quantum-private.h" -#include "MagickCore/resource_.h" -#include "MagickCore/semaphore.h" -#include "MagickCore/static.h" -#include "MagickCore/statistic.h" -#include "MagickCore/string_.h" -#include "MagickCore/string-private.h" -#include "MagickCore/module.h" -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) -#include -#endif - -/* - Forward declarations. - */ -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) -static MagickBooleanType -WriteJP2Image(const ImageInfo *,Image *,ExceptionInfo *); -#endif - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % I s J 2 K % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % IsJ2K() returns MagickTrue if the image format type, identified by the - % magick string, is J2K. - % - % The format of the IsJ2K method is: - % - % MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length) - % - % A description of each parameter follows: - % - % o magick: compare image format pattern against these bytes. - % - % o length: Specifies the length of the magick string. - % - */ -static MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length) -{ - if (length < 4) - return(MagickFalse); - if (memcmp(magick,"\xff\x4f\xff\x51",4) == 0) - return(MagickTrue); - return(MagickFalse); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % I s J P 2 % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % IsJP2() returns MagickTrue if the image format type, identified by the - % magick string, is JP2. - % - % The format of the IsJP2 method is: - % - % MagickBooleanType IsJP2(const unsigned char *magick,const size_t length) - % - % A description of each parameter follows: - % - % o magick: compare image format pattern against these bytes. - % - % o length: Specifies the length of the magick string. - % - */ -static MagickBooleanType IsJP2(const unsigned char *magick,const size_t length) -{ - if (length < 4) - return(MagickFalse); - if (memcmp(magick,"\x0d\x0a\x87\x0a",4) == 0) - return(MagickTrue); - if (length < 12) - return(MagickFalse); - if (memcmp(magick,"\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a",12) == 0) - return(MagickTrue); - return(MagickFalse); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % R e a d J P 2 I m a g e % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % ReadJP2Image() reads a JPEG 2000 Image file (JP2) or JPEG 2000 - % codestream (JPC) image file and returns it. It allocates the memory - % necessary for the new Image structure and returns a pointer to the new - % image or set of images. - % - % JP2 support is originally written by Nathan Brown, nathanbrown@letu.edu. - % - % The format of the ReadJP2Image method is: - % - % Image *ReadJP2Image(const ImageInfo *image_info, - % ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image_info: the image info. - % - % o exception: return any errors or warnings in this structure. - % - */ -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) -static void JP2ErrorHandler(const char *message,void *client_data) -{ - ExceptionInfo - *exception; - - exception=(ExceptionInfo *) client_data; - (void) ThrowMagickException(exception,GetMagickModule(),CoderError, - message,"`%s'","OpenJP2"); -} - -static OPJ_SIZE_T JP2ReadHandler(void *buffer,OPJ_SIZE_T length,void *context) -{ - Image - *image; - - ssize_t - count; - - image=(Image *) context; - count=ReadBlob(image,(ssize_t) length,(unsigned char *) buffer); - if (count == 0) - return((OPJ_SIZE_T) -1); - return((OPJ_SIZE_T) count); -} - -static OPJ_BOOL JP2SeekHandler(OPJ_OFF_T offset,void *context) -{ - Image - *image; - - image=(Image *) context; - return(SeekBlob(image,offset,SEEK_SET) < 0 ? OPJ_FALSE : OPJ_TRUE); -} - -static OPJ_OFF_T JP2SkipHandler(OPJ_OFF_T offset,void *context) -{ - Image - *image; - - image=(Image *) context; - return(SeekBlob(image,offset,SEEK_CUR) < 0 ? -1 : offset); -} - -static void JP2WarningHandler(const char *message,void *client_data) -{ - ExceptionInfo - *exception; - - exception=(ExceptionInfo *) client_data; - (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning, - message,"`%s'","OpenJP2"); -} - -static OPJ_SIZE_T JP2WriteHandler(void *buffer,OPJ_SIZE_T length,void *context) -{ - Image - *image; - - ssize_t - count; - - image=(Image *) context; - count=WriteBlob(image,(ssize_t) length,(unsigned char *) buffer); - return((OPJ_SIZE_T) count); -} - -static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) -{ - const char - *option; - - Image - *image; - - int - jp2_status; - - MagickBooleanType - status; - - opj_codec_t - *jp2_codec; - - opj_dparameters_t - parameters; - - opj_image_t - *jp2_image; - - opj_stream_t - *jp2_stream; - - register ssize_t - i; - - ssize_t - y; - - unsigned char - sans[4]; - - /* - Open image file. - */ - assert(image_info != (const ImageInfo *) NULL); - assert(image_info->signature == MagickCoreSignature); - if (image_info->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", - image_info->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - image=AcquireImage(image_info,exception); - status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); - if (status == MagickFalse) - { - image=DestroyImageList(image); - return((Image *) NULL); - } - /* - Initialize JP2 codec. - */ - if (ReadBlob(image,4,sans) != 4) - { - image=DestroyImageList(image); - return((Image *) NULL); - } - (void) SeekBlob(image,SEEK_SET,0); - if (LocaleCompare(image_info->magick,"JPT") == 0) - jp2_codec=opj_create_decompress(OPJ_CODEC_JPT); - else - if (IsJ2K(sans,4) != MagickFalse) - jp2_codec=opj_create_decompress(OPJ_CODEC_J2K); - else - jp2_codec=opj_create_decompress(OPJ_CODEC_JP2); - opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception); - opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception); - opj_set_default_decoder_parameters(¶meters); - option=GetImageOption(image_info,"jp2:reduce-factor"); - if (option != (const char *) NULL) - parameters.cp_reduce=StringToInteger(option); - option=GetImageOption(image_info,"jp2:quality-layers"); - if (option != (const char *) NULL) - parameters.cp_layer=StringToInteger(option); - if (opj_setup_decoder(jp2_codec,¶meters) == 0) - { - opj_destroy_codec(jp2_codec); - ThrowReaderException(DelegateError,"UnableToManageJP2Stream"); - } - jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,1); - opj_stream_set_read_function(jp2_stream,JP2ReadHandler); - opj_stream_set_write_function(jp2_stream,JP2WriteHandler); - opj_stream_set_seek_function(jp2_stream,JP2SeekHandler); - opj_stream_set_skip_function(jp2_stream,JP2SkipHandler); - opj_stream_set_user_data(jp2_stream,image,NULL); - opj_stream_set_user_data_length(jp2_stream,GetBlobSize(image)); - if (opj_read_header(jp2_stream,jp2_codec,&jp2_image) == 0) - { - opj_stream_destroy(jp2_stream); - opj_destroy_codec(jp2_codec); - ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); - } - jp2_status=OPJ_TRUE; - if (image->ping == MagickFalse) - { - if ((image->columns != 0) && (image->rows != 0)) - /* - Extract an area from the image. - */ - jp2_status=opj_set_decode_area(jp2_codec,jp2_image, - (OPJ_INT32) image->extract_info.x,(OPJ_INT32) image->extract_info.y, - (OPJ_INT32) (image->extract_info.x+(ssize_t) image->columns), - (OPJ_INT32) (image->extract_info.y+(ssize_t) image->rows)); - else - jp2_status=opj_set_decode_area(jp2_codec,jp2_image,0,0, - jp2_image->comps[0].w,jp2_image->comps[0].h); - if (jp2_status == OPJ_FALSE) - { - opj_stream_destroy(jp2_stream); - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); - } - } - if ((AcquireMagickResource(WidthResource,(size_t) jp2_image->comps[0].w) == MagickFalse) || - (AcquireMagickResource(HeightResource,(size_t) jp2_image->comps[0].h) == MagickFalse)) - { - opj_stream_destroy(jp2_stream); - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); - } - if ((image_info->number_scenes != 0) && (image_info->scene != 0)) - jp2_status=opj_get_decoded_tile(jp2_codec,jp2_stream,jp2_image, - (unsigned int) image_info->scene-1); - else - if (image->ping == MagickFalse) - { - jp2_status=opj_decode(jp2_codec,jp2_stream,jp2_image); - if (jp2_status != OPJ_FALSE) - jp2_status=opj_end_decompress(jp2_codec,jp2_stream); - } - if (jp2_status == OPJ_FALSE) - { - opj_stream_destroy(jp2_stream); - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); - } - opj_stream_destroy(jp2_stream); - for (i=0; i < (ssize_t) jp2_image->numcomps; i++) - { - if ((jp2_image->comps[0].dx == 0) || (jp2_image->comps[0].dy == 0) || - (jp2_image->comps[0].prec != jp2_image->comps[i].prec) || - (jp2_image->comps[0].sgnd != jp2_image->comps[i].sgnd) || - ((image->ping == MagickFalse) && (jp2_image->comps[i].data == NULL))) - { - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported") - } - } - /* - Convert JP2 image. - */ - image->columns=(size_t) jp2_image->comps[0].w; - image->rows=(size_t) jp2_image->comps[0].h; - image->depth=jp2_image->comps[0].prec; - image->compression=JPEG2000Compression; - if (jp2_image->numcomps == 1) - SetImageColorspace(image,GRAYColorspace,exception); - else - if (jp2_image->color_space == 2) - { - SetImageColorspace(image,GRAYColorspace,exception); - if (jp2_image->numcomps > 1) - image->alpha_trait=BlendPixelTrait; - } - else - if (jp2_image->color_space == 3) - SetImageColorspace(image,Rec601YCbCrColorspace,exception); - if (jp2_image->numcomps > 3) - image->alpha_trait=BlendPixelTrait; - if (jp2_image->icc_profile_buf != (unsigned char *) NULL) - { - StringInfo - *profile; - - profile=BlobToStringInfo(jp2_image->icc_profile_buf, - jp2_image->icc_profile_len); - if (profile != (StringInfo *) NULL) - { - SetImageProfile(image,"icc",profile,exception); - profile=DestroyStringInfo(profile); - } - } - if (image->ping != MagickFalse) - { - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - return(GetFirstImageInList(image)); - } - status=SetImageExtent(image,image->columns,image->rows,exception); - if (status == MagickFalse) - { - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - return(DestroyImageList(image)); - } - for (y=0; y < (ssize_t) image->rows; y++) - { - register Quantum - *magick_restrict q; - - register ssize_t - x; - - q=GetAuthenticPixels(image,0,y,image->columns,1,exception); - if (q == (Quantum *) NULL) - break; - for (x=0; x < (ssize_t) image->columns; x++) - { - for (i=0; i < (ssize_t) jp2_image->numcomps; i++) - { - double - pixel, - scale; - - scale=QuantumRange/(double) ((1UL << jp2_image->comps[i].prec)-1); - pixel=scale*(jp2_image->comps[i].data[y/jp2_image->comps[i].dy* - image->columns/jp2_image->comps[i].dx+x/jp2_image->comps[i].dx]+ - (jp2_image->comps[i].sgnd ? 1UL << (jp2_image->comps[i].prec-1) : 0)); - switch (i) - { - case 0: - { - if (jp2_image->numcomps == 1) - { - SetPixelGray(image,ClampToQuantum(pixel),q); - SetPixelAlpha(image,OpaqueAlpha,q); - break; - } - SetPixelRed(image,ClampToQuantum(pixel),q); - SetPixelGreen(image,ClampToQuantum(pixel),q); - SetPixelBlue(image,ClampToQuantum(pixel),q); - SetPixelAlpha(image,OpaqueAlpha,q); - break; - } - case 1: - { - if (jp2_image->numcomps == 2) - { - SetPixelAlpha(image,ClampToQuantum(pixel),q); - break; - } - SetPixelGreen(image,ClampToQuantum(pixel),q); - break; - } - case 2: - { - SetPixelBlue(image,ClampToQuantum(pixel),q); - break; - } - case 3: - { - SetPixelAlpha(image,ClampToQuantum(pixel),q); - break; - } - } - } - q+=GetPixelChannels(image); - } - if (SyncAuthenticPixels(image,exception) == MagickFalse) - break; - status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, - image->rows); - if (status == MagickFalse) - break; - } - /* - Free resources. - */ - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - (void) CloseBlob(image); - if ((image_info->number_scenes != 0) && (image_info->scene != 0)) - AppendImageToList(&image,CloneImage(image,0,0,MagickTrue,exception)); - return(GetFirstImageInList(image)); -} -#endif - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % R e g i s t e r J P 2 I m a g e % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % RegisterJP2Image() adds attributes for the JP2 image format to the list of - % supported formats. The attributes include the image format tag, a method - % method to read and/or write the format, whether the format supports the - % saving of more than one frame to the same file or blob, whether the format - % supports native in-memory I/O, and a brief description of the format. - % - % The format of the RegisterJP2Image method is: - % - % size_t RegisterJP2Image(void) - % - */ -ModuleExport size_t RegisterJP2Image(void) -{ - char - version[MagickPathExtent]; - - MagickInfo - *entry; - - *version='\0'; -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) - (void) FormatLocaleString(version,MagickPathExtent,"%s",opj_version()); -#endif - entry=AcquireMagickInfo("JP2","JP2","JPEG-2000 File Format Syntax"); - if (*version != '\0') - entry->version=ConstantString(version); - entry->mime_type=ConstantString("image/jp2"); - entry->magick=(IsImageFormatHandler *) IsJP2; - entry->flags^=CoderAdjoinFlag; - entry->flags|=CoderDecoderSeekableStreamFlag; - entry->flags|=CoderEncoderSeekableStreamFlag; -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) - entry->decoder=(DecodeImageHandler *) ReadJP2Image; - entry->encoder=(EncodeImageHandler *) WriteJP2Image; -#endif - (void) RegisterMagickInfo(entry); - entry=AcquireMagickInfo("JP2","J2C","JPEG-2000 Code Stream Syntax"); - if (*version != '\0') - entry->version=ConstantString(version); - entry->mime_type=ConstantString("image/jp2"); - entry->magick=(IsImageFormatHandler *) IsJ2K; - entry->flags^=CoderAdjoinFlag; - entry->flags|=CoderDecoderSeekableStreamFlag; - entry->flags|=CoderEncoderSeekableStreamFlag; -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) - entry->decoder=(DecodeImageHandler *) ReadJP2Image; - entry->encoder=(EncodeImageHandler *) WriteJP2Image; -#endif - (void) RegisterMagickInfo(entry); - entry=AcquireMagickInfo("JP2","J2K","JPEG-2000 Code Stream Syntax"); - if (*version != '\0') - entry->version=ConstantString(version); - entry->mime_type=ConstantString("image/jp2"); - entry->magick=(IsImageFormatHandler *) IsJ2K; - entry->flags^=CoderAdjoinFlag; - entry->flags|=CoderDecoderSeekableStreamFlag; - entry->flags|=CoderEncoderSeekableStreamFlag; -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) - entry->decoder=(DecodeImageHandler *) ReadJP2Image; - entry->encoder=(EncodeImageHandler *) WriteJP2Image; -#endif - (void) RegisterMagickInfo(entry); - entry=AcquireMagickInfo("JP2","JPM","JPEG-2000 File Format Syntax"); - if (*version != '\0') - entry->version=ConstantString(version); - entry->mime_type=ConstantString("image/jp2"); - entry->magick=(IsImageFormatHandler *) IsJP2; - entry->flags^=CoderAdjoinFlag; - entry->flags|=CoderDecoderSeekableStreamFlag; - entry->flags|=CoderEncoderSeekableStreamFlag; -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) - entry->decoder=(DecodeImageHandler *) ReadJP2Image; - entry->encoder=(EncodeImageHandler *) WriteJP2Image; -#endif - (void) RegisterMagickInfo(entry); - entry=AcquireMagickInfo("JP2","JPT","JPEG-2000 File Format Syntax"); - if (*version != '\0') - entry->version=ConstantString(version); - entry->mime_type=ConstantString("image/jp2"); - entry->magick=(IsImageFormatHandler *) IsJP2; - entry->flags^=CoderAdjoinFlag; - entry->flags|=CoderDecoderSeekableStreamFlag; - entry->flags|=CoderEncoderSeekableStreamFlag; -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) - entry->decoder=(DecodeImageHandler *) ReadJP2Image; - entry->encoder=(EncodeImageHandler *) WriteJP2Image; -#endif - (void) RegisterMagickInfo(entry); - entry=AcquireMagickInfo("JP2","JPC","JPEG-2000 Code Stream Syntax"); - if (*version != '\0') - entry->version=ConstantString(version); - entry->mime_type=ConstantString("image/jp2"); - entry->magick=(IsImageFormatHandler *) IsJP2; - entry->flags^=CoderAdjoinFlag; - entry->flags|=CoderDecoderSeekableStreamFlag; - entry->flags|=CoderEncoderSeekableStreamFlag; -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) - entry->decoder=(DecodeImageHandler *) ReadJP2Image; - entry->encoder=(EncodeImageHandler *) WriteJP2Image; -#endif - (void) RegisterMagickInfo(entry); - return(MagickImageCoderSignature); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % U n r e g i s t e r J P 2 I m a g e % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % UnregisterJP2Image() removes format registrations made by the JP2 module - % from the list of supported formats. - % - % The format of the UnregisterJP2Image method is: - % - % UnregisterJP2Image(void) - % - */ -ModuleExport void UnregisterJP2Image(void) -{ - (void) UnregisterMagickInfo("JPC"); - (void) UnregisterMagickInfo("JPT"); - (void) UnregisterMagickInfo("JPM"); - (void) UnregisterMagickInfo("JP2"); - (void) UnregisterMagickInfo("J2K"); -} - -#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % W r i t e J P 2 I m a g e % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % WriteJP2Image() writes an image in the JPEG 2000 image format. - % - % JP2 support originally written by Nathan Brown, nathanbrown@letu.edu - % - % The format of the WriteJP2Image method is: - % - % MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, - % ExceptionInfo *exception) - % - % A description of each parameter follows. - % - % o image_info: the image info. - % - % o image: The image. - % - */ - -static void CinemaProfileCompliance(const opj_image_t *jp2_image, - opj_cparameters_t *parameters) -{ - /* - Digital Cinema 4K profile compliant codestream. - */ - parameters->tile_size_on=OPJ_FALSE; - parameters->cp_tdx=1; - parameters->cp_tdy=1; - parameters->tp_flag='C'; - parameters->tp_on=1; - parameters->cp_tx0=0; - parameters->cp_ty0=0; - parameters->image_offset_x0=0; - parameters->image_offset_y0=0; - parameters->cblockw_init=32; - parameters->cblockh_init=32; - parameters->csty|=0x01; - parameters->prog_order=OPJ_CPRL; - parameters->roi_compno=(-1); - parameters->subsampling_dx=1; - parameters->subsampling_dy=1; - parameters->irreversible=1; - if ((jp2_image->comps[0].w == 2048) || (jp2_image->comps[0].h == 1080)) - { - /* - Digital Cinema 2K. - */ - parameters->cp_cinema=OPJ_CINEMA2K_24; - parameters->cp_rsiz=OPJ_CINEMA2K; - parameters->max_comp_size=1041666; - if (parameters->numresolution > 6) - parameters->numresolution=6; - - } - if ((jp2_image->comps[0].w == 4096) || (jp2_image->comps[0].h == 2160)) - { - /* - Digital Cinema 4K. - */ - parameters->cp_cinema=OPJ_CINEMA4K_24; - parameters->cp_rsiz=OPJ_CINEMA4K; - parameters->max_comp_size=1041666; - if (parameters->numresolution < 1) - parameters->numresolution=1; - if (parameters->numresolution > 7) - parameters->numresolution=7; - parameters->numpocs=2; - parameters->POC[0].tile=1; - parameters->POC[0].resno0=0; - parameters->POC[0].compno0=0; - parameters->POC[0].layno1=1; - parameters->POC[0].resno1=parameters->numresolution-1; - parameters->POC[0].compno1=3; - parameters->POC[0].prg1=OPJ_CPRL; - parameters->POC[1].tile=1; - parameters->POC[1].resno0=parameters->numresolution-1; - parameters->POC[1].compno0=0; - parameters->POC[1].layno1=1; - parameters->POC[1].resno1=parameters->numresolution; - parameters->POC[1].compno1=3; - parameters->POC[1].prg1=OPJ_CPRL; - } - parameters->tcp_numlayers=1; - parameters->tcp_rates[0]=((float) (jp2_image->numcomps*jp2_image->comps[0].w* - jp2_image->comps[0].h*jp2_image->comps[0].prec))/(parameters->max_comp_size* - 8*jp2_image->comps[0].dx*jp2_image->comps[0].dy); - parameters->cp_disto_alloc=1; -} - -static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, - ExceptionInfo *exception) -{ - const char - *option, - *property; - - int - jp2_status; - - MagickBooleanType - status; - - opj_codec_t - *jp2_codec; - - OPJ_COLOR_SPACE - jp2_colorspace; - - opj_cparameters_t - parameters; - - opj_image_cmptparm_t - jp2_info[5]; - - opj_image_t - *jp2_image; - - opj_stream_t - *jp2_stream; - - register ssize_t - i; - - ssize_t - y; - - unsigned int - channels; - - /* - Open image file. - */ - assert(image_info != (const ImageInfo *) NULL); - assert(image_info->signature == MagickCoreSignature); - assert(image != (Image *) NULL); - assert(image->signature == MagickCoreSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); - if (status == MagickFalse) - return(status); - /* - Initialize JPEG 2000 API. - */ - opj_set_default_encoder_parameters(¶meters); - for (i=1; i < 6; i++) - if (((size_t) (1UL << (i+2)) > image->columns) && - ((size_t) (1UL << (i+2)) > image->rows)) - break; - parameters.numresolution=i; - option=GetImageOption(image_info,"jp2:number-resolutions"); - if (option != (const char *) NULL) - parameters.numresolution=StringToInteger(option); - parameters.tcp_numlayers=1; - parameters.tcp_rates[0]=0; /* lossless */ - parameters.cp_disto_alloc=1; - if ((image_info->quality != 0) && (image_info->quality != 100)) - { - parameters.tcp_distoratio[0]=(double) image_info->quality; - parameters.cp_fixed_quality=OPJ_TRUE; - } - if (image_info->extract != (char *) NULL) - { - RectangleInfo - geometry; - - int - flags; - - /* - Set tile size. - */ - flags=ParseAbsoluteGeometry(image_info->extract,&geometry); - parameters.cp_tdx=(int) geometry.width; - parameters.cp_tdy=(int) geometry.width; - if ((flags & HeightValue) != 0) - parameters.cp_tdy=(int) geometry.height; - if ((flags & XValue) != 0) - parameters.cp_tx0=geometry.x; - if ((flags & YValue) != 0) - parameters.cp_ty0=geometry.y; - parameters.tile_size_on=OPJ_TRUE; - } - option=GetImageOption(image_info,"jp2:quality"); - if (option != (const char *) NULL) - { - register const char - *p; - - /* - Set quality PSNR. - */ - p=option; - for (i=0; sscanf(p,"%f",¶meters.tcp_distoratio[i]) == 1; i++) - { - if (i > 100) - break; - while ((*p != '\0') && (*p != ',')) - p++; - if (*p == '\0') - break; - p++; - } - parameters.tcp_numlayers=i+1; - parameters.cp_fixed_quality=OPJ_TRUE; - } - option=GetImageOption(image_info,"jp2:progression-order"); - if (option != (const char *) NULL) - { - if (LocaleCompare(option,"LRCP") == 0) - parameters.prog_order=OPJ_LRCP; - if (LocaleCompare(option,"RLCP") == 0) - parameters.prog_order=OPJ_RLCP; - if (LocaleCompare(option,"RPCL") == 0) - parameters.prog_order=OPJ_RPCL; - if (LocaleCompare(option,"PCRL") == 0) - parameters.prog_order=OPJ_PCRL; - if (LocaleCompare(option,"CPRL") == 0) - parameters.prog_order=OPJ_CPRL; - } - option=GetImageOption(image_info,"jp2:rate"); - if (option != (const char *) NULL) - { - register const char - *p; - - /* - Set compression rate. - */ - p=option; - for (i=0; sscanf(p,"%f",¶meters.tcp_rates[i]) == 1; i++) - { - if (i >= 100) - break; - while ((*p != '\0') && (*p != ',')) - p++; - if (*p == '\0') - break; - p++; - } - parameters.tcp_numlayers=i+1; - parameters.cp_disto_alloc=OPJ_TRUE; - } - if (image_info->sampling_factor != (const char *) NULL) - (void) sscanf(image_info->sampling_factor,"%d,%d", - ¶meters.subsampling_dx,¶meters.subsampling_dy); - property=GetImageProperty(image,"comment",exception); - if (property != (const char *) NULL) - parameters.cp_comment=(char *) property; - channels=3; - jp2_colorspace=OPJ_CLRSPC_SRGB; - if (image->colorspace == YUVColorspace) - { - jp2_colorspace=OPJ_CLRSPC_SYCC; - parameters.subsampling_dx=2; - } - else - { - if (IsGrayColorspace(image->colorspace) != MagickFalse) - { - channels=1; - jp2_colorspace=OPJ_CLRSPC_GRAY; - } - else - (void) TransformImageColorspace(image,sRGBColorspace,exception); - if (image->alpha_trait != UndefinedPixelTrait) - channels++; - } - parameters.tcp_mct=channels == 3 ? 1 : 0; - memset(jp2_info,0,sizeof(jp2_info)); - for (i=0; i < (ssize_t) channels; i++) - { - jp2_info[i].prec=(OPJ_UINT32) image->depth; - jp2_info[i].bpp=(OPJ_UINT32) image->depth; - if ((image->depth == 1) && - ((LocaleCompare(image_info->magick,"JPT") == 0) || - (LocaleCompare(image_info->magick,"JP2") == 0))) - { - jp2_info[i].prec++; /* OpenJPEG returns exception for depth @ 1 */ - jp2_info[i].bpp++; - } - jp2_info[i].sgnd=0; - jp2_info[i].dx=parameters.subsampling_dx; - jp2_info[i].dy=parameters.subsampling_dy; - jp2_info[i].w=(OPJ_UINT32) image->columns; - jp2_info[i].h=(OPJ_UINT32) image->rows; - } - jp2_image=opj_image_create((OPJ_UINT32) channels,jp2_info,jp2_colorspace); - if (jp2_image == (opj_image_t *) NULL) - ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); - jp2_image->x0=parameters.image_offset_x0; - jp2_image->y0=parameters.image_offset_y0; - jp2_image->x1=(unsigned int) (2*parameters.image_offset_x0+(image->columns-1)* - parameters.subsampling_dx+1); - jp2_image->y1=(unsigned int) (2*parameters.image_offset_y0+(image->rows-1)* - parameters.subsampling_dx+1); - if ((image->depth == 12) && - ((image->columns == 2048) || (image->rows == 1080) || - (image->columns == 4096) || (image->rows == 2160))) - CinemaProfileCompliance(jp2_image,¶meters); - if (channels == 4) - jp2_image->comps[3].alpha=1; - else - if ((channels == 2) && (jp2_colorspace == OPJ_CLRSPC_GRAY)) - jp2_image->comps[1].alpha=1; - /* - Convert to JP2 pixels. - */ - for (y=0; y < (ssize_t) image->rows; y++) - { - register const Quantum - *p; - - ssize_t - x; - - p=GetVirtualPixels(image,0,y,image->columns,1,exception); - if (p == (const Quantum *) NULL) - break; - for (x=0; x < (ssize_t) image->columns; x++) - { - for (i=0; i < (ssize_t) channels; i++) - { - double - scale; - - register int - *q; - - scale=(double) ((1UL << jp2_image->comps[i].prec)-1)/QuantumRange; - q=jp2_image->comps[i].data+(y/jp2_image->comps[i].dy* - image->columns/jp2_image->comps[i].dx+x/jp2_image->comps[i].dx); - switch (i) - { - case 0: - { - if (jp2_colorspace == OPJ_CLRSPC_GRAY) - { - *q=(int) (scale*GetPixelGray(image,p)); - break; - } - *q=(int) (scale*GetPixelRed(image,p)); - break; - } - case 1: - { - if (jp2_colorspace == OPJ_CLRSPC_GRAY) - { - *q=(int) (scale*GetPixelAlpha(image,p)); - break; - } - *q=(int) (scale*GetPixelGreen(image,p)); - break; - } - case 2: - { - *q=(int) (scale*GetPixelBlue(image,p)); - break; - } - case 3: - { - *q=(int) (scale*GetPixelAlpha(image,p)); - break; - } - } - } - p+=GetPixelChannels(image); - } - status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, - image->rows); - if (status == MagickFalse) - break; - } - if (LocaleCompare(image_info->magick,"JPT") == 0) - jp2_codec=opj_create_compress(OPJ_CODEC_JPT); - else - if (LocaleCompare(image_info->magick,"J2K") == 0) - jp2_codec=opj_create_compress(OPJ_CODEC_J2K); - else - jp2_codec=opj_create_compress(OPJ_CODEC_JP2); - opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception); - opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception); - opj_setup_encoder(jp2_codec,¶meters,jp2_image); - jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,OPJ_FALSE); - if (jp2_stream == (opj_stream_t *) NULL) - { - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); - } - opj_stream_set_read_function(jp2_stream,JP2ReadHandler); - opj_stream_set_write_function(jp2_stream,JP2WriteHandler); - opj_stream_set_seek_function(jp2_stream,JP2SeekHandler); - opj_stream_set_skip_function(jp2_stream,JP2SkipHandler); - opj_stream_set_user_data(jp2_stream,image,NULL); - jp2_status=opj_start_compress(jp2_codec,jp2_image,jp2_stream); - if ((jp2_status == 0) || (opj_encode(jp2_codec,jp2_stream) == 0) || - (opj_end_compress(jp2_codec,jp2_stream) == 0)) - { - opj_stream_destroy(jp2_stream); - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); - } - /* - Free resources. - */ - opj_stream_destroy(jp2_stream); - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - (void) CloseBlob(image); - return(MagickTrue); -} -#endif diff --git a/test/bug-hunting/cve/CVE-2019-13390/README b/test/bug-hunting/cve/CVE-2019-13390/README deleted file mode 100644 index c417df0cfae..00000000000 --- a/test/bug-hunting/cve/CVE-2019-13390/README +++ /dev/null @@ -1,4 +0,0 @@ -Details: -https://nvd.nist.gov/vuln/detail/CVE-2019-13390 - - diff --git a/test/bug-hunting/cve/CVE-2019-13390/cmd.txt b/test/bug-hunting/cve/CVE-2019-13390/cmd.txt deleted file mode 100644 index 1cf4f7c4fcc..00000000000 --- a/test/bug-hunting/cve/CVE-2019-13390/cmd.txt +++ /dev/null @@ -1 +0,0 @@ --DCONFIG_ADX_MUXER=1 diff --git a/test/bug-hunting/cve/CVE-2019-13390/expected.txt b/test/bug-hunting/cve/CVE-2019-13390/expected.txt deleted file mode 100644 index ac4e31270b1..00000000000 --- a/test/bug-hunting/cve/CVE-2019-13390/expected.txt +++ /dev/null @@ -1 +0,0 @@ -libavformat_rawenc.c:70:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-13390/libavformat_rawenc.c b/test/bug-hunting/cve/CVE-2019-13390/libavformat_rawenc.c deleted file mode 100644 index b74d1118cbb..00000000000 --- a/test/bug-hunting/cve/CVE-2019-13390/libavformat_rawenc.c +++ /dev/null @@ -1,505 +0,0 @@ -/* - * RAW muxers - * Copyright (c) 2001 Fabrice Bellard - * Copyright (c) 2005 Alex Beregszaszi - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with FFmpeg; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "libavutil/intreadwrite.h" - -#include "avformat.h" -#include "rawenc.h" -#include "internal.h" - -int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt) -{ - avio_write(s->pb, pkt->data, pkt->size); - return 0; -} - -static int force_one_stream(AVFormatContext *s) -{ - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "%s files have exactly one stream\n", - s->oformat->name); - return AVERROR(EINVAL); - } - return 0; -} - -/* Note: Do not forget to add new entries to the Makefile as well. */ - -#if CONFIG_AC3_MUXER -AVOutputFormat ff_ac3_muxer = { - .name = "ac3", - .long_name = NULL_IF_CONFIG_SMALL("raw AC-3"), - .mime_type = "audio/x-ac3", - .extensions = "ac3", - .audio_codec = AV_CODEC_ID_AC3, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_ADX_MUXER - -static int adx_write_trailer(AVFormatContext *s) -{ - AVIOContext *pb = s->pb; - AVCodecParameters *par = s->streams[0]->codecpar; - - if (pb->seekable & AVIO_SEEKABLE_NORMAL) { - int64_t file_size = avio_tell(pb); - uint64_t sample_count = (file_size - 36) / par->channels / 18 * 32; - if (sample_count <= UINT32_MAX) { - avio_seek(pb, 12, SEEK_SET); - avio_wb32(pb, sample_count); - avio_seek(pb, file_size, SEEK_SET); - } - } - - return 0; -} - -AVOutputFormat ff_adx_muxer = { - .name = "adx", - .long_name = NULL_IF_CONFIG_SMALL("CRI ADX"), - .extensions = "adx", - .audio_codec = AV_CODEC_ID_ADPCM_ADX, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .write_trailer = adx_write_trailer, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_APTX_MUXER -AVOutputFormat ff_aptx_muxer = { - .name = "aptx", - .long_name = NULL_IF_CONFIG_SMALL("raw aptX (Audio Processing Technology for Bluetooth)"), - .extensions = "aptx", - .audio_codec = AV_CODEC_ID_APTX, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_APTX_HD_MUXER -AVOutputFormat ff_aptx_hd_muxer = { - .name = "aptx_hd", - .long_name = NULL_IF_CONFIG_SMALL("raw aptX HD (Audio Processing Technology for Bluetooth)"), - .extensions = "aptxhd", - .audio_codec = AV_CODEC_ID_APTX_HD, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_AVS2_MUXER -AVOutputFormat ff_avs2_muxer = { - .name = "avs2", - .long_name = NULL_IF_CONFIG_SMALL("raw AVS2-P2/IEEE1857.4 video"), - .extensions = "avs,avs2", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_AVS2, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_CAVSVIDEO_MUXER -AVOutputFormat ff_cavsvideo_muxer = { - .name = "cavsvideo", - .long_name = NULL_IF_CONFIG_SMALL("raw Chinese AVS (Audio Video Standard) video"), - .extensions = "cavs", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_CAVS, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_CODEC2RAW_MUXER -AVOutputFormat ff_codec2raw_muxer = { - .name = "codec2raw", - .long_name = NULL_IF_CONFIG_SMALL("raw codec2 muxer"), - .audio_codec = AV_CODEC_ID_CODEC2, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - - -#if CONFIG_DATA_MUXER -AVOutputFormat ff_data_muxer = { - .name = "data", - .long_name = NULL_IF_CONFIG_SMALL("raw data"), - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_DIRAC_MUXER -AVOutputFormat ff_dirac_muxer = { - .name = "dirac", - .long_name = NULL_IF_CONFIG_SMALL("raw Dirac"), - .extensions = "drc,vc2", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_DIRAC, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_DNXHD_MUXER -AVOutputFormat ff_dnxhd_muxer = { - .name = "dnxhd", - .long_name = NULL_IF_CONFIG_SMALL("raw DNxHD (SMPTE VC-3)"), - .extensions = "dnxhd,dnxhr", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_DNXHD, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_DTS_MUXER -AVOutputFormat ff_dts_muxer = { - .name = "dts", - .long_name = NULL_IF_CONFIG_SMALL("raw DTS"), - .mime_type = "audio/x-dca", - .extensions = "dts", - .audio_codec = AV_CODEC_ID_DTS, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_EAC3_MUXER -AVOutputFormat ff_eac3_muxer = { - .name = "eac3", - .long_name = NULL_IF_CONFIG_SMALL("raw E-AC-3"), - .mime_type = "audio/x-eac3", - .extensions = "eac3", - .audio_codec = AV_CODEC_ID_EAC3, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_G722_MUXER -AVOutputFormat ff_g722_muxer = { - .name = "g722", - .long_name = NULL_IF_CONFIG_SMALL("raw G.722"), - .mime_type = "audio/G722", - .extensions = "g722", - .audio_codec = AV_CODEC_ID_ADPCM_G722, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_G723_1_MUXER -AVOutputFormat ff_g723_1_muxer = { - .name = "g723_1", - .long_name = NULL_IF_CONFIG_SMALL("raw G.723.1"), - .mime_type = "audio/g723", - .extensions = "tco,rco", - .audio_codec = AV_CODEC_ID_G723_1, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_G726_MUXER -AVOutputFormat ff_g726_muxer = { - .name = "g726", - .long_name = NULL_IF_CONFIG_SMALL("raw big-endian G.726 (\"left-justified\")"), - .audio_codec = AV_CODEC_ID_ADPCM_G726, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_G726LE_MUXER -AVOutputFormat ff_g726le_muxer = { - .name = "g726le", - .long_name = NULL_IF_CONFIG_SMALL("raw little-endian G.726 (\"right-justified\")"), - .audio_codec = AV_CODEC_ID_ADPCM_G726LE, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_GSM_MUXER -AVOutputFormat ff_gsm_muxer = { - .name = "gsm", - .long_name = NULL_IF_CONFIG_SMALL("raw GSM"), - .mime_type = "audio/x-gsm", - .extensions = "gsm", - .audio_codec = AV_CODEC_ID_GSM, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_H261_MUXER -AVOutputFormat ff_h261_muxer = { - .name = "h261", - .long_name = NULL_IF_CONFIG_SMALL("raw H.261"), - .mime_type = "video/x-h261", - .extensions = "h261", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_H261, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_H263_MUXER -AVOutputFormat ff_h263_muxer = { - .name = "h263", - .long_name = NULL_IF_CONFIG_SMALL("raw H.263"), - .mime_type = "video/x-h263", - .extensions = "h263", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_H263, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_H264_MUXER -static int h264_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) -{ - AVStream *st = s->streams[0]; - if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 && - AV_RB24(pkt->data) != 0x000001) - return ff_stream_add_bitstream_filter(st, "h264_mp4toannexb", NULL); - return 1; -} - -AVOutputFormat ff_h264_muxer = { - .name = "h264", - .long_name = NULL_IF_CONFIG_SMALL("raw H.264 video"), - .extensions = "h264,264", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_H264, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .check_bitstream = h264_check_bitstream, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_HEVC_MUXER -static int hevc_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) -{ - AVStream *st = s->streams[0]; - if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 && - AV_RB24(pkt->data) != 0x000001) - return ff_stream_add_bitstream_filter(st, "hevc_mp4toannexb", NULL); - return 1; -} - -AVOutputFormat ff_hevc_muxer = { - .name = "hevc", - .long_name = NULL_IF_CONFIG_SMALL("raw HEVC video"), - .extensions = "hevc,h265,265", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_HEVC, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .check_bitstream = hevc_check_bitstream, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_M4V_MUXER -AVOutputFormat ff_m4v_muxer = { - .name = "m4v", - .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-4 video"), - .extensions = "m4v", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_MPEG4, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_MJPEG_MUXER -AVOutputFormat ff_mjpeg_muxer = { - .name = "mjpeg", - .long_name = NULL_IF_CONFIG_SMALL("raw MJPEG video"), - .mime_type = "video/x-mjpeg", - .extensions = "mjpg,mjpeg", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_MJPEG, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_SINGLEJPEG_MUXER -AVOutputFormat ff_singlejpeg_muxer = { - .name = "singlejpeg", - .long_name = NULL_IF_CONFIG_SMALL("JPEG single image"), - .mime_type = "image/jpeg", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_MJPEG, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, - .write_header = force_one_stream, -}; -#endif - -#if CONFIG_MLP_MUXER -AVOutputFormat ff_mlp_muxer = { - .name = "mlp", - .long_name = NULL_IF_CONFIG_SMALL("raw MLP"), - .extensions = "mlp", - .audio_codec = AV_CODEC_ID_MLP, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_MP2_MUXER -AVOutputFormat ff_mp2_muxer = { - .name = "mp2", - .long_name = NULL_IF_CONFIG_SMALL("MP2 (MPEG audio layer 2)"), - .mime_type = "audio/mpeg", - .extensions = "mp2,m2a,mpa", - .audio_codec = AV_CODEC_ID_MP2, - .video_codec = AV_CODEC_ID_NONE, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_MPEG1VIDEO_MUXER -AVOutputFormat ff_mpeg1video_muxer = { - .name = "mpeg1video", - .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-1 video"), - .mime_type = "video/mpeg", - .extensions = "mpg,mpeg,m1v", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_MPEG1VIDEO, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_MPEG2VIDEO_MUXER -AVOutputFormat ff_mpeg2video_muxer = { - .name = "mpeg2video", - .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-2 video"), - .extensions = "m2v", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_MPEG2VIDEO, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_RAWVIDEO_MUXER -AVOutputFormat ff_rawvideo_muxer = { - .name = "rawvideo", - .long_name = NULL_IF_CONFIG_SMALL("raw video"), - .extensions = "yuv,rgb", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_SBC_MUXER -AVOutputFormat ff_sbc_muxer = { - .name = "sbc", - .long_name = NULL_IF_CONFIG_SMALL("raw SBC"), - .mime_type = "audio/x-sbc", - .extensions = "sbc,msbc", - .audio_codec = AV_CODEC_ID_SBC, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_TRUEHD_MUXER -AVOutputFormat ff_truehd_muxer = { - .name = "truehd", - .long_name = NULL_IF_CONFIG_SMALL("raw TrueHD"), - .extensions = "thd", - .audio_codec = AV_CODEC_ID_TRUEHD, - .video_codec = AV_CODEC_ID_NONE, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif - -#if CONFIG_VC1_MUXER -AVOutputFormat ff_vc1_muxer = { - .name = "vc1", - .long_name = NULL_IF_CONFIG_SMALL("raw VC-1 video"), - .extensions = "vc1", - .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_VC1, - .write_header = force_one_stream, - .write_packet = ff_raw_write_packet, - .flags = AVFMT_NOTIMESTAMPS, -}; -#endif diff --git a/test/bug-hunting/cve/CVE-2019-13454/expected.txt b/test/bug-hunting/cve/CVE-2019-13454/expected.txt deleted file mode 100644 index 09bafadcc0a..00000000000 --- a/test/bug-hunting/cve/CVE-2019-13454/expected.txt +++ /dev/null @@ -1,3 +0,0 @@ -layer.c:1616:bughuntingDivByZero -layer.c:1617:bughuntingDivByZero - diff --git a/test/bug-hunting/cve/CVE-2019-13454/layer.c b/test/bug-hunting/cve/CVE-2019-13454/layer.c deleted file mode 100644 index 6d1cc9a3f07..00000000000 --- a/test/bug-hunting/cve/CVE-2019-13454/layer.c +++ /dev/null @@ -1,2055 +0,0 @@ -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % L AAA Y Y EEEEE RRRR % - % L A A Y Y E R R % - % L AAAAA Y EEE RRRR % - % L A A Y E R R % - % LLLLL A A Y EEEEE R R % - % % - % MagickCore Image Layering Methods % - % % - % Software Design % - % Cristy % - % Anthony Thyssen % - % January 2006 % - % % - % % - % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization % - % dedicated to making software imaging solutions freely available. % - % % - % You may not use this file except in compliance with the License. You may % - % obtain a copy of the License at % - % % - % https://imagemagick.org/script/license.php % - % % - % Unless required by applicable law or agreed to in writing, software % - % distributed under the License is distributed on an "AS IS" BASIS, % - % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % - % See the License for the specific language governing permissions and % - % limitations under the License. % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - */ - -/* - Include declarations. - */ -#include "MagickCore/studio.h" -#include "MagickCore/artifact.h" -#include "MagickCore/cache.h" -#include "MagickCore/channel.h" -#include "MagickCore/color.h" -#include "MagickCore/color-private.h" -#include "MagickCore/composite.h" -#include "MagickCore/effect.h" -#include "MagickCore/exception.h" -#include "MagickCore/exception-private.h" -#include "MagickCore/geometry.h" -#include "MagickCore/image.h" -#include "MagickCore/layer.h" -#include "MagickCore/list.h" -#include "MagickCore/memory_.h" -#include "MagickCore/monitor.h" -#include "MagickCore/monitor-private.h" -#include "MagickCore/option.h" -#include "MagickCore/pixel-accessor.h" -#include "MagickCore/property.h" -#include "MagickCore/profile.h" -#include "MagickCore/resource_.h" -#include "MagickCore/resize.h" -#include "MagickCore/statistic.h" -#include "MagickCore/string_.h" -#include "MagickCore/transform.h" - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - + C l e a r B o u n d s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % ClearBounds() Clear the area specified by the bounds in an image to - % transparency. This typically used to handle Background Disposal for the - % previous frame in an animation sequence. - % - % Warning: no bounds checks are performed, except for the null or missed - % image, for images that don't change. in all other cases bound must fall - % within the image. - % - % The format is: - % - % void ClearBounds(Image *image,RectangleInfo *bounds, - % ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image to had the area cleared in - % - % o bounds: the area to be clear within the imag image - % - % o exception: return any errors or warnings in this structure. - % - */ -static void ClearBounds(Image *image,RectangleInfo *bounds, - ExceptionInfo *exception) -{ - ssize_t - y; - - if (bounds->x < 0) - return; - if (image->alpha_trait == UndefinedPixelTrait) - (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); - for (y=0; y < (ssize_t) bounds->height; y++) - { - register ssize_t - x; - - register Quantum - *magick_restrict q; - - q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception); - if (q == (Quantum *) NULL) - break; - for (x=0; x < (ssize_t) bounds->width; x++) - { - SetPixelAlpha(image,TransparentAlpha,q); - q+=GetPixelChannels(image); - } - if (SyncAuthenticPixels(image,exception) == MagickFalse) - break; - } -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - + I s B o u n d s C l e a r e d % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % IsBoundsCleared() tests whether any pixel in the bounds given, gets cleared - % when going from the first image to the second image. This typically used - % to check if a proposed disposal method will work successfully to generate - % the second frame image from the first disposed form of the previous frame. - % - % Warning: no bounds checks are performed, except for the null or missed - % image, for images that don't change. in all other cases bound must fall - % within the image. - % - % The format is: - % - % MagickBooleanType IsBoundsCleared(const Image *image1, - % const Image *image2,RectangleInfo bounds,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image1, image 2: the images to check for cleared pixels - % - % o bounds: the area to be clear within the imag image - % - % o exception: return any errors or warnings in this structure. - % - */ -static MagickBooleanType IsBoundsCleared(const Image *image1, - const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception) -{ - register const Quantum - *p, - *q; - - register ssize_t - x; - - ssize_t - y; - - if (bounds->x < 0) - return(MagickFalse); - for (y=0; y < (ssize_t) bounds->height; y++) - { - p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,exception); - q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,exception); - if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) - break; - for (x=0; x < (ssize_t) bounds->width; x++) - { - if ((GetPixelAlpha(image1,p) >= (Quantum) (QuantumRange/2)) && - (GetPixelAlpha(image2,q) < (Quantum) (QuantumRange/2))) - break; - p+=GetPixelChannels(image1); - q+=GetPixelChannels(image2); - } - if (x < (ssize_t) bounds->width) - break; - } - return(y < (ssize_t) bounds->height ? MagickTrue : MagickFalse); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % C o a l e s c e I m a g e s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % CoalesceImages() composites a set of images while respecting any page - % offsets and disposal methods. GIF, MIFF, and MNG animation sequences - % typically start with an image background and each subsequent image - % varies in size and offset. A new image sequence is returned with all - % images the same size as the first images virtual canvas and composited - % with the next image in the sequence. - % - % The format of the CoalesceImages method is: - % - % Image *CoalesceImages(Image *image,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image sequence. - % - % o exception: return any errors or warnings in this structure. - % - */ -MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception) -{ - Image - *coalesce_image, - *dispose_image, - *previous; - - register Image - *next; - - RectangleInfo - bounds; - - /* - Coalesce the image sequence. - */ - assert(image != (Image *) NULL); - assert(image->signature == MagickCoreSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - next=GetFirstImageInList(image); - bounds=next->page; - if (bounds.width == 0) - { - bounds.width=next->columns; - if (bounds.x > 0) - bounds.width+=bounds.x; - } - if (bounds.height == 0) - { - bounds.height=next->rows; - if (bounds.y > 0) - bounds.height+=bounds.y; - } - bounds.x=0; - bounds.y=0; - coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue, - exception); - if (coalesce_image == (Image *) NULL) - return((Image *) NULL); - coalesce_image->background_color.alpha=(MagickRealType) TransparentAlpha; - (void) SetImageBackgroundColor(coalesce_image,exception); - coalesce_image->alpha_trait=next->alpha_trait; - coalesce_image->page=bounds; - coalesce_image->dispose=NoneDispose; - /* - Coalesce rest of the images. - */ - dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception); - (void) CompositeImage(coalesce_image,next,CopyCompositeOp,MagickTrue, - next->page.x,next->page.y,exception); - next=GetNextImageInList(next); - for ( ; next != (Image *) NULL; next=GetNextImageInList(next)) - { - /* - Determine the bounds that was overlaid in the previous image. - */ - previous=GetPreviousImageInList(next); - bounds=previous->page; - bounds.width=previous->columns; - bounds.height=previous->rows; - if (bounds.x < 0) - { - bounds.width+=bounds.x; - bounds.x=0; - } - if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) coalesce_image->columns) - bounds.width=coalesce_image->columns-bounds.x; - if (bounds.y < 0) - { - bounds.height+=bounds.y; - bounds.y=0; - } - if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) coalesce_image->rows) - bounds.height=coalesce_image->rows-bounds.y; - /* - Replace the dispose image with the new coalesced image. - */ - if (GetPreviousImageInList(next)->dispose != PreviousDispose) - { - dispose_image=DestroyImage(dispose_image); - dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception); - if (dispose_image == (Image *) NULL) - { - coalesce_image=DestroyImageList(coalesce_image); - return((Image *) NULL); - } - } - /* - Clear the overlaid area of the coalesced bounds for background disposal - */ - if (next->previous->dispose == BackgroundDispose) - ClearBounds(dispose_image,&bounds,exception); - /* - Next image is the dispose image, overlaid with next frame in sequence. - */ - coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception); - coalesce_image->next->previous=coalesce_image; - previous=coalesce_image; - coalesce_image=GetNextImageInList(coalesce_image); - (void) CompositeImage(coalesce_image,next, - next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp, - MagickTrue,next->page.x,next->page.y,exception); - (void) CloneImageProfiles(coalesce_image,next); - (void) CloneImageProperties(coalesce_image,next); - (void) CloneImageArtifacts(coalesce_image,next); - coalesce_image->page=previous->page; - /* - If a pixel goes opaque to transparent, use background dispose. - */ - if (IsBoundsCleared(previous,coalesce_image,&bounds,exception) != MagickFalse) - coalesce_image->dispose=BackgroundDispose; - else - coalesce_image->dispose=NoneDispose; - previous->dispose=coalesce_image->dispose; - } - dispose_image=DestroyImage(dispose_image); - return(GetFirstImageInList(coalesce_image)); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % D i s p o s e I m a g e s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % DisposeImages() returns the coalesced frames of a GIF animation as it would - % appear after the GIF dispose method of that frame has been applied. That is - % it returned the appearance of each frame before the next is overlaid. - % - % The format of the DisposeImages method is: - % - % Image *DisposeImages(Image *image,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o images: the image sequence. - % - % o exception: return any errors or warnings in this structure. - % - */ -MagickExport Image *DisposeImages(const Image *images,ExceptionInfo *exception) -{ - Image - *dispose_image, - *dispose_images; - - RectangleInfo - bounds; - - register Image - *image, - *next; - - /* - Run the image through the animation sequence - */ - assert(images != (Image *) NULL); - assert(images->signature == MagickCoreSignature); - if (images->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - image=GetFirstImageInList(images); - dispose_image=CloneImage(image,image->page.width,image->page.height, - MagickTrue,exception); - if (dispose_image == (Image *) NULL) - return((Image *) NULL); - dispose_image->page=image->page; - dispose_image->page.x=0; - dispose_image->page.y=0; - dispose_image->dispose=NoneDispose; - dispose_image->background_color.alpha=(MagickRealType) TransparentAlpha; - (void) SetImageBackgroundColor(dispose_image,exception); - dispose_images=NewImageList(); - for (next=image; image != (Image *) NULL; image=GetNextImageInList(image)) - { - Image - *current_image; - - /* - Overlay this frame's image over the previous disposal image. - */ - current_image=CloneImage(dispose_image,0,0,MagickTrue,exception); - if (current_image == (Image *) NULL) - { - dispose_images=DestroyImageList(dispose_images); - dispose_image=DestroyImage(dispose_image); - return((Image *) NULL); - } - (void) CompositeImage(current_image,next, - next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp, - MagickTrue,next->page.x,next->page.y,exception); - /* - Handle Background dispose: image is displayed for the delay period. - */ - if (next->dispose == BackgroundDispose) - { - bounds=next->page; - bounds.width=next->columns; - bounds.height=next->rows; - if (bounds.x < 0) - { - bounds.width+=bounds.x; - bounds.x=0; - } - if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns) - bounds.width=current_image->columns-bounds.x; - if (bounds.y < 0) - { - bounds.height+=bounds.y; - bounds.y=0; - } - if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows) - bounds.height=current_image->rows-bounds.y; - ClearBounds(current_image,&bounds,exception); - } - /* - Select the appropriate previous/disposed image. - */ - if (next->dispose == PreviousDispose) - current_image=DestroyImage(current_image); - else - { - dispose_image=DestroyImage(dispose_image); - dispose_image=current_image; - current_image=(Image *) NULL; - } - /* - Save the dispose image just calculated for return. - */ - { - Image - *dispose; - - dispose=CloneImage(dispose_image,0,0,MagickTrue,exception); - if (dispose == (Image *) NULL) - { - dispose_images=DestroyImageList(dispose_images); - dispose_image=DestroyImage(dispose_image); - return((Image *) NULL); - } - (void) CloneImageProfiles(dispose,next); - (void) CloneImageProperties(dispose,next); - (void) CloneImageArtifacts(dispose,next); - dispose->page.x=0; - dispose->page.y=0; - dispose->dispose=next->dispose; - AppendImageToList(&dispose_images,dispose); - } - } - dispose_image=DestroyImage(dispose_image); - return(GetFirstImageInList(dispose_images)); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - + C o m p a r e P i x e l s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % ComparePixels() Compare the two pixels and return true if the pixels - % differ according to the given LayerType comparision method. - % - % This currently only used internally by CompareImagesBounds(). It is - % doubtful that this sub-routine will be useful outside this module. - % - % The format of the ComparePixels method is: - % - % MagickBooleanType *ComparePixels(const LayerMethod method, - % const PixelInfo *p,const PixelInfo *q) - % - % A description of each parameter follows: - % - % o method: What differences to look for. Must be one of - % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer. - % - % o p, q: the pixels to test for appropriate differences. - % - */ - -static MagickBooleanType ComparePixels(const LayerMethod method, - const PixelInfo *p,const PixelInfo *q) -{ - double - o1, - o2; - - /* - Any change in pixel values - */ - if (method == CompareAnyLayer) - return((MagickBooleanType)(IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse)); - - o1 = (p->alpha_trait != UndefinedPixelTrait) ? p->alpha : OpaqueAlpha; - o2 = (q->alpha_trait != UndefinedPixelTrait) ? q->alpha : OpaqueAlpha; - /* - Pixel goes from opaque to transprency. - */ - if (method == CompareClearLayer) - return((MagickBooleanType) ((o1 >= ((double) QuantumRange/2.0)) && - (o2 < ((double) QuantumRange/2.0)))); - /* - Overlay would change first pixel by second. - */ - if (method == CompareOverlayLayer) - { - if (o2 < ((double) QuantumRange/2.0)) - return MagickFalse; - return((MagickBooleanType) (IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse)); - } - return(MagickFalse); -} - - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - + C o m p a r e I m a g e B o u n d s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % CompareImagesBounds() Given two images return the smallest rectangular area - % by which the two images differ, accourding to the given 'Compare...' - % layer method. - % - % This currently only used internally in this module, but may eventually - % be used by other modules. - % - % The format of the CompareImagesBounds method is: - % - % RectangleInfo *CompareImagesBounds(const LayerMethod method, - % const Image *image1,const Image *image2,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o method: What differences to look for. Must be one of CompareAnyLayer, - % CompareClearLayer, CompareOverlayLayer. - % - % o image1, image2: the two images to compare. - % - % o exception: return any errors or warnings in this structure. - % - */ - -static RectangleInfo CompareImagesBounds(const Image *image1, - const Image *image2,const LayerMethod method,ExceptionInfo *exception) -{ - RectangleInfo - bounds; - - PixelInfo - pixel1, - pixel2; - - register const Quantum - *p, - *q; - - register ssize_t - x; - - ssize_t - y; - - /* - Set bounding box of the differences between images. - */ - GetPixelInfo(image1,&pixel1); - GetPixelInfo(image2,&pixel2); - for (x=0; x < (ssize_t) image1->columns; x++) - { - p=GetVirtualPixels(image1,x,0,1,image1->rows,exception); - q=GetVirtualPixels(image2,x,0,1,image2->rows,exception); - if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) - break; - for (y=0; y < (ssize_t) image1->rows; y++) - { - GetPixelInfoPixel(image1,p,&pixel1); - GetPixelInfoPixel(image2,q,&pixel2); - if (ComparePixels(method,&pixel1,&pixel2)) - break; - p+=GetPixelChannels(image1); - q+=GetPixelChannels(image2); - } - if (y < (ssize_t) image1->rows) - break; - } - if (x >= (ssize_t) image1->columns) - { - /* - Images are identical, return a null image. - */ - bounds.x=-1; - bounds.y=-1; - bounds.width=1; - bounds.height=1; - return(bounds); - } - bounds.x=x; - for (x=(ssize_t) image1->columns-1; x >= 0; x--) - { - p=GetVirtualPixels(image1,x,0,1,image1->rows,exception); - q=GetVirtualPixels(image2,x,0,1,image2->rows,exception); - if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) - break; - for (y=0; y < (ssize_t) image1->rows; y++) - { - GetPixelInfoPixel(image1,p,&pixel1); - GetPixelInfoPixel(image2,q,&pixel2); - if (ComparePixels(method,&pixel1,&pixel2)) - break; - p+=GetPixelChannels(image1); - q+=GetPixelChannels(image2); - } - if (y < (ssize_t) image1->rows) - break; - } - bounds.width=(size_t) (x-bounds.x+1); - for (y=0; y < (ssize_t) image1->rows; y++) - { - p=GetVirtualPixels(image1,0,y,image1->columns,1,exception); - q=GetVirtualPixels(image2,0,y,image2->columns,1,exception); - if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) - break; - for (x=0; x < (ssize_t) image1->columns; x++) - { - GetPixelInfoPixel(image1,p,&pixel1); - GetPixelInfoPixel(image2,q,&pixel2); - if (ComparePixels(method,&pixel1,&pixel2)) - break; - p+=GetPixelChannels(image1); - q+=GetPixelChannels(image2); - } - if (x < (ssize_t) image1->columns) - break; - } - bounds.y=y; - for (y=(ssize_t) image1->rows-1; y >= 0; y--) - { - p=GetVirtualPixels(image1,0,y,image1->columns,1,exception); - q=GetVirtualPixels(image2,0,y,image2->columns,1,exception); - if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) - break; - for (x=0; x < (ssize_t) image1->columns; x++) - { - GetPixelInfoPixel(image1,p,&pixel1); - GetPixelInfoPixel(image2,q,&pixel2); - if (ComparePixels(method,&pixel1,&pixel2)) - break; - p+=GetPixelChannels(image1); - q+=GetPixelChannels(image2); - } - if (x < (ssize_t) image1->columns) - break; - } - bounds.height=(size_t) (y-bounds.y+1); - return(bounds); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % C o m p a r e I m a g e L a y e r s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % CompareImagesLayers() compares each image with the next in a sequence and - % returns the minimum bounding region of all the pixel differences (of the - % LayerMethod specified) it discovers. - % - % Images do NOT have to be the same size, though it is best that all the - % images are 'coalesced' (images are all the same size, on a flattened - % canvas, so as to represent exactly how an specific frame should look). - % - % No GIF dispose methods are applied, so GIF animations must be coalesced - % before applying this image operator to find differences to them. - % - % The format of the CompareImagesLayers method is: - % - % Image *CompareImagesLayers(const Image *images, - % const LayerMethod method,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image. - % - % o method: the layers type to compare images with. Must be one of... - % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer. - % - % o exception: return any errors or warnings in this structure. - % - */ - -MagickExport Image *CompareImagesLayers(const Image *image, - const LayerMethod method,ExceptionInfo *exception) -{ - Image - *image_a, - *image_b, - *layers; - - RectangleInfo - *bounds; - - register const Image - *next; - - register ssize_t - i; - - assert(image != (const Image *) NULL); - assert(image->signature == MagickCoreSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - assert((method == CompareAnyLayer) || - (method == CompareClearLayer) || - (method == CompareOverlayLayer)); - /* - Allocate bounds memory. - */ - next=GetFirstImageInList(image); - bounds=(RectangleInfo *) AcquireQuantumMemory((size_t) - GetImageListLength(next),sizeof(*bounds)); - if (bounds == (RectangleInfo *) NULL) - ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); - /* - Set up first comparision images. - */ - image_a=CloneImage(next,next->page.width,next->page.height, - MagickTrue,exception); - if (image_a == (Image *) NULL) - { - bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); - return((Image *) NULL); - } - image_a->background_color.alpha=(MagickRealType) TransparentAlpha; - (void) SetImageBackgroundColor(image_a,exception); - image_a->page=next->page; - image_a->page.x=0; - image_a->page.y=0; - (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x, - next->page.y,exception); - /* - Compute the bounding box of changes for the later images - */ - i=0; - next=GetNextImageInList(next); - for ( ; next != (const Image *) NULL; next=GetNextImageInList(next)) - { - image_b=CloneImage(image_a,0,0,MagickTrue,exception); - if (image_b == (Image *) NULL) - { - image_a=DestroyImage(image_a); - bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); - return((Image *) NULL); - } - (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x, - next->page.y,exception); - bounds[i]=CompareImagesBounds(image_b,image_a,method,exception); - image_b=DestroyImage(image_b); - i++; - } - image_a=DestroyImage(image_a); - /* - Clone first image in sequence. - */ - next=GetFirstImageInList(image); - layers=CloneImage(next,0,0,MagickTrue,exception); - if (layers == (Image *) NULL) - { - bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); - return((Image *) NULL); - } - /* - Deconstruct the image sequence. - */ - i=0; - next=GetNextImageInList(next); - for ( ; next != (const Image *) NULL; next=GetNextImageInList(next)) - { - if ((bounds[i].x == -1) && (bounds[i].y == -1) && - (bounds[i].width == 1) && (bounds[i].height == 1)) - { - /* - An empty frame is returned from CompareImageBounds(), which means the - current frame is identical to the previous frame. - */ - i++; - continue; - } - image_a=CloneImage(next,0,0,MagickTrue,exception); - if (image_a == (Image *) NULL) - break; - image_b=CropImage(image_a,&bounds[i],exception); - image_a=DestroyImage(image_a); - if (image_b == (Image *) NULL) - break; - AppendImageToList(&layers,image_b); - i++; - } - bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); - if (next != (Image *) NULL) - { - layers=DestroyImageList(layers); - return((Image *) NULL); - } - return(GetFirstImageInList(layers)); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - + O p t i m i z e L a y e r F r a m e s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % OptimizeLayerFrames() takes a coalesced GIF animation, and compares each - % frame against the three different 'disposal' forms of the previous frame. - % From this it then attempts to select the smallest cropped image and - % disposal method needed to reproduce the resulting image. - % - % Note that this not easy, and may require the expansion of the bounds - % of previous frame, simply clear pixels for the next animation frame to - % transparency according to the selected dispose method. - % - % The format of the OptimizeLayerFrames method is: - % - % Image *OptimizeLayerFrames(const Image *image, - % const LayerMethod method,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image. - % - % o method: the layers technique to optimize with. Must be one of... - % OptimizeImageLayer, or OptimizePlusLayer. The Plus form allows - % the addition of extra 'zero delay' frames to clear pixels from - % the previous frame, and the removal of frames that done change, - % merging the delay times together. - % - % o exception: return any errors or warnings in this structure. - % - */ -/* - Define a 'fake' dispose method where the frame is duplicated, (for - OptimizePlusLayer) with a extra zero time delay frame which does a - BackgroundDisposal to clear the pixels that need to be cleared. - */ -#define DupDispose ((DisposeType)9) -/* - Another 'fake' dispose method used to removed frames that don't change. - */ -#define DelDispose ((DisposeType)8) - -#define DEBUG_OPT_FRAME 0 - -static Image *OptimizeLayerFrames(const Image *image,const LayerMethod method, - ExceptionInfo *exception) -{ - ExceptionInfo - *sans_exception; - - Image - *prev_image, - *dup_image, - *bgnd_image, - *optimized_image; - - RectangleInfo - try_bounds, - bgnd_bounds, - dup_bounds, - *bounds; - - MagickBooleanType - add_frames, - try_cleared, - cleared; - - DisposeType - *disposals; - - register const Image - *curr; - - register ssize_t - i; - - assert(image != (const Image *) NULL); - assert(image->signature == MagickCoreSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - assert(method == OptimizeLayer || - method == OptimizeImageLayer || - method == OptimizePlusLayer); - /* - Are we allowed to add/remove frames from animation? - */ - add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse; - /* - Ensure all the images are the same size. - */ - curr=GetFirstImageInList(image); - for (; curr != (Image *) NULL; curr=GetNextImageInList(curr)) - { - if ((curr->columns != image->columns) || (curr->rows != image->rows)) - ThrowImageException(OptionError,"ImagesAreNotTheSameSize"); - - if ((curr->page.x != 0) || (curr->page.y != 0) || - (curr->page.width != image->page.width) || - (curr->page.height != image->page.height)) - ThrowImageException(OptionError,"ImagePagesAreNotCoalesced"); - } - /* - Allocate memory (times 2 if we allow the use of frame duplications) - */ - curr=GetFirstImageInList(image); - bounds=(RectangleInfo *) AcquireQuantumMemory((size_t) - GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)* - sizeof(*bounds)); - if (bounds == (RectangleInfo *) NULL) - ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); - disposals=(DisposeType *) AcquireQuantumMemory((size_t) - GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)* - sizeof(*disposals)); - if (disposals == (DisposeType *) NULL) - { - bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); - ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); - } - /* - Initialise Previous Image as fully transparent - */ - prev_image=CloneImage(curr,curr->columns,curr->rows,MagickTrue,exception); - if (prev_image == (Image *) NULL) - { - bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); - disposals=(DisposeType *) RelinquishMagickMemory(disposals); - return((Image *) NULL); - } - prev_image->page=curr->page; /* ERROR: <-- should not be need, but is! */ - prev_image->page.x=0; - prev_image->page.y=0; - prev_image->dispose=NoneDispose; - prev_image->background_color.alpha_trait=BlendPixelTrait; - prev_image->background_color.alpha=(MagickRealType) TransparentAlpha; - (void) SetImageBackgroundColor(prev_image,exception); - /* - Figure out the area of overlay of the first frame - No pixel could be cleared as all pixels are already cleared. - */ -#if DEBUG_OPT_FRAME - i=0; - (void) FormatLocaleFile(stderr,"frame %.20g :-\n",(double) i); -#endif - disposals[0]=NoneDispose; - bounds[0]=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception); -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g\n\n", - (double) bounds[i].width,(double) bounds[i].height, - (double) bounds[i].x,(double) bounds[i].y ); -#endif - /* - Compute the bounding box of changes for each pair of images. - */ - i=1; - bgnd_image=(Image *) NULL; - dup_image=(Image *) NULL; - dup_bounds.width=0; - dup_bounds.height=0; - dup_bounds.x=0; - dup_bounds.y=0; - curr=GetNextImageInList(curr); - for ( ; curr != (const Image *) NULL; curr=GetNextImageInList(curr)) - { -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr,"frame %.20g :-\n",(double) i); -#endif - /* - Assume none disposal is the best - */ - bounds[i]=CompareImagesBounds(curr->previous,curr,CompareAnyLayer,exception); - cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception); - disposals[i-1]=NoneDispose; -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g%s%s\n", - (double) bounds[i].width,(double) bounds[i].height, - (double) bounds[i].x,(double) bounds[i].y, - bounds[i].x < 0?" (unchanged)":"", - cleared?" (pixels cleared)":""); -#endif - if (bounds[i].x < 0) { - /* - Image frame is exactly the same as the previous frame! - If not adding frames leave it to be cropped down to a null image. - Otherwise mark previous image for deleted, transfering its crop bounds - to the current image. - */ - if (add_frames && i>=2) { - disposals[i-1]=DelDispose; - disposals[i]=NoneDispose; - bounds[i]=bounds[i-1]; - i++; - continue; - } - } - else - { - /* - Compare a none disposal against a previous disposal - */ - try_bounds=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception); - try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception); -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr, "test_prev: %.20gx%.20g%+.20g%+.20g%s\n", - (double) try_bounds.width,(double) try_bounds.height, - (double) try_bounds.x,(double) try_bounds.y, - try_cleared?" (pixels were cleared)":""); -#endif - if ((!try_cleared && cleared) || - try_bounds.width * try_bounds.height - < bounds[i].width * bounds[i].height) - { - cleared=try_cleared; - bounds[i]=try_bounds; - disposals[i-1]=PreviousDispose; -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr,"previous: accepted\n"); - } else { - (void) FormatLocaleFile(stderr,"previous: rejected\n"); -#endif - } - - /* - If we are allowed lets try a complex frame duplication. - It is useless if the previous image already clears pixels correctly. - This method will always clear all the pixels that need to be cleared. - */ - dup_bounds.width=dup_bounds.height=0; /* no dup, no pixel added */ - if (add_frames) - { - dup_image=CloneImage(curr->previous,0,0,MagickTrue,exception); - if (dup_image == (Image *) NULL) - { - bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); - disposals=(DisposeType *) RelinquishMagickMemory(disposals); - prev_image=DestroyImage(prev_image); - return((Image *) NULL); - } - dup_bounds=CompareImagesBounds(dup_image,curr,CompareClearLayer,exception); - ClearBounds(dup_image,&dup_bounds,exception); - try_bounds=CompareImagesBounds(dup_image,curr,CompareAnyLayer,exception); - if (cleared || - dup_bounds.width*dup_bounds.height - +try_bounds.width*try_bounds.height - < bounds[i].width * bounds[i].height) - { - cleared=MagickFalse; - bounds[i]=try_bounds; - disposals[i-1]=DupDispose; - /* to be finalised later, if found to be optimial */ - } - else - dup_bounds.width=dup_bounds.height=0; - } - /* - Now compare against a simple background disposal - */ - bgnd_image=CloneImage(curr->previous,0,0,MagickTrue,exception); - if (bgnd_image == (Image *) NULL) - { - bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); - disposals=(DisposeType *) RelinquishMagickMemory(disposals); - prev_image=DestroyImage(prev_image); - if (dup_image != (Image *) NULL) - dup_image=DestroyImage(dup_image); - return((Image *) NULL); - } - bgnd_bounds=bounds[i-1]; /* interum bounds of the previous image */ - ClearBounds(bgnd_image,&bgnd_bounds,exception); - try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception); - try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception); -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr, "background: %s\n", - try_cleared?"(pixels cleared)":""); -#endif - if (try_cleared) - { - /* - Straight background disposal failed to clear pixels needed! - Lets try expanding the disposal area of the previous frame, to - include the pixels that are cleared. This guaranteed - to work, though may not be the most optimized solution. - */ - try_bounds=CompareImagesBounds(curr->previous,curr,CompareClearLayer,exception); -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr, "expand_clear: %.20gx%.20g%+.20g%+.20g%s\n", - (double) try_bounds.width,(double) try_bounds.height, - (double) try_bounds.x,(double) try_bounds.y, - try_bounds.x<0?" (no expand nessary)":""); -#endif - if (bgnd_bounds.x < 0) - bgnd_bounds = try_bounds; - else - { -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr, "expand_bgnd: %.20gx%.20g%+.20g%+.20g\n", - (double) bgnd_bounds.width,(double) bgnd_bounds.height, - (double) bgnd_bounds.x,(double) bgnd_bounds.y ); -#endif - if (try_bounds.x < bgnd_bounds.x) - { - bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x; - if (bgnd_bounds.width < try_bounds.width) - bgnd_bounds.width = try_bounds.width; - bgnd_bounds.x = try_bounds.x; - } - else - { - try_bounds.width += try_bounds.x - bgnd_bounds.x; - if (bgnd_bounds.width < try_bounds.width) - bgnd_bounds.width = try_bounds.width; - } - if (try_bounds.y < bgnd_bounds.y) - { - bgnd_bounds.height += bgnd_bounds.y - try_bounds.y; - if (bgnd_bounds.height < try_bounds.height) - bgnd_bounds.height = try_bounds.height; - bgnd_bounds.y = try_bounds.y; - } - else - { - try_bounds.height += try_bounds.y - bgnd_bounds.y; - if (bgnd_bounds.height < try_bounds.height) - bgnd_bounds.height = try_bounds.height; - } -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr, " to : %.20gx%.20g%+.20g%+.20g\n", - (double) bgnd_bounds.width,(double) bgnd_bounds.height, - (double) bgnd_bounds.x,(double) bgnd_bounds.y ); -#endif - } - ClearBounds(bgnd_image,&bgnd_bounds,exception); -#if DEBUG_OPT_FRAME -/* Something strange is happening with a specific animation - * CompareAnyLayers (normal method) and CompareClearLayers returns the whole - * image, which is not posibly correct! As verified by previous tests. - * Something changed beyond the bgnd_bounds clearing. But without being able - * to see, or writet he image at this point it is hard to tell what is wrong! - * Only CompareOverlay seemed to return something sensible. - */ - try_bounds=CompareImagesBounds(bgnd_image,curr,CompareClearLayer,exception); - (void) FormatLocaleFile(stderr, "expand_ctst: %.20gx%.20g%+.20g%+.20g\n", - (double) try_bounds.width,(double) try_bounds.height, - (double) try_bounds.x,(double) try_bounds.y ); - try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception); - try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception); - (void) FormatLocaleFile(stderr, "expand_any : %.20gx%.20g%+.20g%+.20g%s\n", - (double) try_bounds.width,(double) try_bounds.height, - (double) try_bounds.x,(double) try_bounds.y, - try_cleared?" (pixels cleared)":""); -#endif - try_bounds=CompareImagesBounds(bgnd_image,curr,CompareOverlayLayer,exception); -#if DEBUG_OPT_FRAME - try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception); - (void) FormatLocaleFile(stderr, "expand_test: %.20gx%.20g%+.20g%+.20g%s\n", - (double) try_bounds.width,(double) try_bounds.height, - (double) try_bounds.x,(double) try_bounds.y, - try_cleared?" (pixels cleared)":""); -#endif - } - /* - Test if this background dispose is smaller than any of the - other methods we tryed before this (including duplicated frame) - */ - if (cleared || - bgnd_bounds.width*bgnd_bounds.height - +try_bounds.width*try_bounds.height - < bounds[i-1].width*bounds[i-1].height - +dup_bounds.width*dup_bounds.height - +bounds[i].width*bounds[i].height) - { - cleared=MagickFalse; - bounds[i-1]=bgnd_bounds; - bounds[i]=try_bounds; - if (disposals[i-1] == DupDispose) - dup_image=DestroyImage(dup_image); - disposals[i-1]=BackgroundDispose; -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr,"expand_bgnd: accepted\n"); - } else { - (void) FormatLocaleFile(stderr,"expand_bgnd: reject\n"); -#endif - } - } - /* - Finalise choice of dispose, set new prev_image, - and junk any extra images as appropriate, - */ - if (disposals[i-1] == DupDispose) - { - if (bgnd_image != (Image *) NULL) - bgnd_image=DestroyImage(bgnd_image); - prev_image=DestroyImage(prev_image); - prev_image=dup_image, dup_image=(Image *) NULL; - bounds[i+1]=bounds[i]; - bounds[i]=dup_bounds; - disposals[i-1]=DupDispose; - disposals[i]=BackgroundDispose; - i++; - } - else - { - if (dup_image != (Image *) NULL) - dup_image=DestroyImage(dup_image); - if (disposals[i-1] != PreviousDispose) - prev_image=DestroyImage(prev_image); - if (disposals[i-1] == BackgroundDispose) - prev_image=bgnd_image, bgnd_image=(Image *) NULL; - if (bgnd_image != (Image *) NULL) - bgnd_image=DestroyImage(bgnd_image); - if (disposals[i-1] == NoneDispose) - { - prev_image=ReferenceImage(curr->previous); - if (prev_image == (Image *) NULL) - { - bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); - disposals=(DisposeType *) RelinquishMagickMemory(disposals); - return((Image *) NULL); - } - } - - } - assert(prev_image != (Image *) NULL); - disposals[i]=disposals[i-1]; -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr, "final %.20g : %s %.20gx%.20g%+.20g%+.20g\n", - (double) i-1, - CommandOptionToMnemonic(MagickDisposeOptions,disposals[i-1]), - (double) bounds[i-1].width,(double) bounds[i-1].height, - (double) bounds[i-1].x,(double) bounds[i-1].y ); -#endif -#if DEBUG_OPT_FRAME - (void) FormatLocaleFile(stderr, "interum %.20g : %s %.20gx%.20g%+.20g%+.20g\n", - (double) i, - CommandOptionToMnemonic(MagickDisposeOptions,disposals[i]), - (double) bounds[i].width,(double) bounds[i].height, - (double) bounds[i].x,(double) bounds[i].y ); - (void) FormatLocaleFile(stderr,"\n"); -#endif - i++; - } - prev_image=DestroyImage(prev_image); - /* - Optimize all images in sequence. - */ - sans_exception=AcquireExceptionInfo(); - i=0; - curr=GetFirstImageInList(image); - optimized_image=NewImageList(); - while (curr != (const Image *) NULL) - { - prev_image=CloneImage(curr,0,0,MagickTrue,exception); - if (prev_image == (Image *) NULL) - break; - if (prev_image->alpha_trait == UndefinedPixelTrait) - (void) SetImageAlphaChannel(prev_image,OpaqueAlphaChannel,exception); - if (disposals[i] == DelDispose) { - size_t time = 0; - while (disposals[i] == DelDispose) { - time += curr->delay*1000/curr->ticks_per_second; - curr=GetNextImageInList(curr); - i++; - } - time += curr->delay*1000/curr->ticks_per_second; - prev_image->ticks_per_second = 100L; - prev_image->delay = time*prev_image->ticks_per_second/1000; - } - bgnd_image=CropImage(prev_image,&bounds[i],sans_exception); - prev_image=DestroyImage(prev_image); - if (bgnd_image == (Image *) NULL) - break; - bgnd_image->dispose=disposals[i]; - if (disposals[i] == DupDispose) { - bgnd_image->delay=0; - bgnd_image->dispose=NoneDispose; - } - else - curr=GetNextImageInList(curr); - AppendImageToList(&optimized_image,bgnd_image); - i++; - } - sans_exception=DestroyExceptionInfo(sans_exception); - bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); - disposals=(DisposeType *) RelinquishMagickMemory(disposals); - if (curr != (Image *) NULL) - { - optimized_image=DestroyImageList(optimized_image); - return((Image *) NULL); - } - return(GetFirstImageInList(optimized_image)); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % O p t i m i z e I m a g e L a y e r s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % OptimizeImageLayers() compares each image the GIF disposed forms of the - % previous image in the sequence. From this it attempts to select the - % smallest cropped image to replace each frame, while preserving the results - % of the GIF animation. - % - % The format of the OptimizeImageLayers method is: - % - % Image *OptimizeImageLayers(const Image *image, - % ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image. - % - % o exception: return any errors or warnings in this structure. - % - */ -MagickExport Image *OptimizeImageLayers(const Image *image, - ExceptionInfo *exception) -{ - return(OptimizeLayerFrames(image,OptimizeImageLayer,exception)); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % O p t i m i z e P l u s I m a g e L a y e r s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % OptimizeImagePlusLayers() is exactly as OptimizeImageLayers(), but may - % also add or even remove extra frames in the animation, if it improves - % the total number of pixels in the resulting GIF animation. - % - % The format of the OptimizePlusImageLayers method is: - % - % Image *OptimizePlusImageLayers(const Image *image, - % ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image. - % - % o exception: return any errors or warnings in this structure. - % - */ -MagickExport Image *OptimizePlusImageLayers(const Image *image, - ExceptionInfo *exception) -{ - return OptimizeLayerFrames(image,OptimizePlusLayer,exception); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % O p t i m i z e I m a g e T r a n s p a r e n c y % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % OptimizeImageTransparency() takes a frame optimized GIF animation, and - % compares the overlayed pixels against the disposal image resulting from all - % the previous frames in the animation. Any pixel that does not change the - % disposal image (and thus does not effect the outcome of an overlay) is made - % transparent. - % - % WARNING: This modifies the current images directly, rather than generate - % a new image sequence. - % - % The format of the OptimizeImageTransperency method is: - % - % void OptimizeImageTransperency(Image *image,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image sequence - % - % o exception: return any errors or warnings in this structure. - % - */ -MagickExport void OptimizeImageTransparency(const Image *image, - ExceptionInfo *exception) -{ - Image - *dispose_image; - - register Image - *next; - - /* - Run the image through the animation sequence - */ - assert(image != (Image *) NULL); - assert(image->signature == MagickCoreSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - next=GetFirstImageInList(image); - dispose_image=CloneImage(next,next->page.width,next->page.height, - MagickTrue,exception); - if (dispose_image == (Image *) NULL) - return; - dispose_image->page=next->page; - dispose_image->page.x=0; - dispose_image->page.y=0; - dispose_image->dispose=NoneDispose; - dispose_image->background_color.alpha_trait=BlendPixelTrait; - dispose_image->background_color.alpha=(MagickRealType) TransparentAlpha; - (void) SetImageBackgroundColor(dispose_image,exception); - - while (next != (Image *) NULL) - { - Image - *current_image; - - /* - Overlay this frame's image over the previous disposal image - */ - current_image=CloneImage(dispose_image,0,0,MagickTrue,exception); - if (current_image == (Image *) NULL) - { - dispose_image=DestroyImage(dispose_image); - return; - } - (void) CompositeImage(current_image,next,next->alpha_trait != UndefinedPixelTrait ? - OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y, - exception); - /* - At this point the image would be displayed, for the delay period - ** - Work out the disposal of the previous image - */ - if (next->dispose == BackgroundDispose) - { - RectangleInfo - bounds=next->page; - - bounds.width=next->columns; - bounds.height=next->rows; - if (bounds.x < 0) - { - bounds.width+=bounds.x; - bounds.x=0; - } - if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns) - bounds.width=current_image->columns-bounds.x; - if (bounds.y < 0) - { - bounds.height+=bounds.y; - bounds.y=0; - } - if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows) - bounds.height=current_image->rows-bounds.y; - ClearBounds(current_image,&bounds,exception); - } - if (next->dispose != PreviousDispose) - { - dispose_image=DestroyImage(dispose_image); - dispose_image=current_image; - } - else - current_image=DestroyImage(current_image); - - /* - Optimize Transparency of the next frame (if present) - */ - next=GetNextImageInList(next); - if (next != (Image *) NULL) { - (void) CompositeImage(next,dispose_image,ChangeMaskCompositeOp, - MagickTrue,-(next->page.x),-(next->page.y),exception); - } - } - dispose_image=DestroyImage(dispose_image); - return; -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % R e m o v e D u p l i c a t e L a y e r s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % RemoveDuplicateLayers() removes any image that is exactly the same as the - % next image in the given image list. Image size and virtual canvas offset - % must also match, though not the virtual canvas size itself. - % - % No check is made with regards to image disposal setting, though it is the - % dispose setting of later image that is kept. Also any time delays are also - % added together. As such coalesced image animations should still produce the - % same result, though with duplicte frames merged into a single frame. - % - % The format of the RemoveDuplicateLayers method is: - % - % void RemoveDuplicateLayers(Image **image,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o images: the image list - % - % o exception: return any errors or warnings in this structure. - % - */ -MagickExport void RemoveDuplicateLayers(Image **images, - ExceptionInfo *exception) -{ - register Image - *curr, - *next; - - RectangleInfo - bounds; - - assert((*images) != (const Image *) NULL); - assert((*images)->signature == MagickCoreSignature); - if ((*images)->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - - curr=GetFirstImageInList(*images); - for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next) - { - if (curr->columns != next->columns || curr->rows != next->rows - || curr->page.x != next->page.x || curr->page.y != next->page.y) - continue; - bounds=CompareImagesBounds(curr,next,CompareAnyLayer,exception); - if (bounds.x < 0) { - /* - the two images are the same, merge time delays and delete one. - */ - size_t time; - time = curr->delay*1000/curr->ticks_per_second; - time += next->delay*1000/next->ticks_per_second; - next->ticks_per_second = 100L; - next->delay = time*curr->ticks_per_second/1000; - next->iterations = curr->iterations; - *images = curr; - (void) DeleteImageFromList(images); - } - } - *images = GetFirstImageInList(*images); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % R e m o v e Z e r o D e l a y L a y e r s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % RemoveZeroDelayLayers() removes any image that as a zero delay time. Such - % images generally represent intermediate or partial updates in GIF - % animations used for file optimization. They are not ment to be displayed - % to users of the animation. Viewable images in an animation should have a - % time delay of 3 or more centi-seconds (hundredths of a second). - % - % However if all the frames have a zero time delay, then either the animation - % is as yet incomplete, or it is not a GIF animation. This a non-sensible - % situation, so no image will be removed and a 'Zero Time Animation' warning - % (exception) given. - % - % No warning will be given if no image was removed because all images had an - % appropriate non-zero time delay set. - % - % Due to the special requirements of GIF disposal handling, GIF animations - % should be coalesced first, before calling this function, though that is not - % a requirement. - % - % The format of the RemoveZeroDelayLayers method is: - % - % void RemoveZeroDelayLayers(Image **image,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o images: the image list - % - % o exception: return any errors or warnings in this structure. - % - */ -MagickExport void RemoveZeroDelayLayers(Image **images, - ExceptionInfo *exception) -{ - Image - *i; - - assert((*images) != (const Image *) NULL); - assert((*images)->signature == MagickCoreSignature); - if ((*images)->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - - i=GetFirstImageInList(*images); - for ( ; i != (Image *) NULL; i=GetNextImageInList(i)) - if (i->delay != 0L) break; - if (i == (Image *) NULL) { - (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, - "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename); - return; - } - i=GetFirstImageInList(*images); - while (i != (Image *) NULL) - { - if (i->delay == 0L) { - (void) DeleteImageFromList(&i); - *images=i; - } - else - i=GetNextImageInList(i); - } - *images=GetFirstImageInList(*images); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % C o m p o s i t e L a y e r s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % CompositeLayers() compose the source image sequence over the destination - % image sequence, starting with the current image in both lists. - % - % Each layer from the two image lists are composted together until the end of - % one of the image lists is reached. The offset of each composition is also - % adjusted to match the virtual canvas offsets of each layer. As such the - % given offset is relative to the virtual canvas, and not the actual image. - % - % Composition uses given x and y offsets, as the 'origin' location of the - % source images virtual canvas (not the real image) allowing you to compose a - % list of 'layer images' into the destiantioni images. This makes it well - % sutiable for directly composing 'Clears Frame Animations' or 'Coaleased - % Animations' onto a static or other 'Coaleased Animation' destination image - % list. GIF disposal handling is not looked at. - % - % Special case:- If one of the image sequences is the last image (just a - % single image remaining), that image is repeatally composed with all the - % images in the other image list. Either the source or destination lists may - % be the single image, for this situation. - % - % In the case of a single destination image (or last image given), that image - % will ve cloned to match the number of images remaining in the source image - % list. - % - % This is equivelent to the "-layer Composite" Shell API operator. - % - % - % The format of the CompositeLayers method is: - % - % void CompositeLayers(Image *destination, const CompositeOperator - % compose, Image *source, const ssize_t x_offset, const ssize_t y_offset, - % ExceptionInfo *exception); - % - % A description of each parameter follows: - % - % o destination: the destination images and results - % - % o source: source image(s) for the layer composition - % - % o compose, x_offset, y_offset: arguments passed on to CompositeImages() - % - % o exception: return any errors or warnings in this structure. - % - */ - -static inline void CompositeCanvas(Image *destination, - const CompositeOperator compose,Image *source,ssize_t x_offset, - ssize_t y_offset,ExceptionInfo *exception) -{ - const char - *value; - - x_offset+=source->page.x-destination->page.x; - y_offset+=source->page.y-destination->page.y; - value=GetImageArtifact(source,"compose:outside-overlay"); - (void) CompositeImage(destination,source,compose, - (value != (const char *) NULL) && (IsStringTrue(value) != MagickFalse) ? - MagickFalse : MagickTrue,x_offset,y_offset,exception); -} - -MagickExport void CompositeLayers(Image *destination, - const CompositeOperator compose, Image *source,const ssize_t x_offset, - const ssize_t y_offset,ExceptionInfo *exception) -{ - assert(destination != (Image *) NULL); - assert(destination->signature == MagickCoreSignature); - assert(source != (Image *) NULL); - assert(source->signature == MagickCoreSignature); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - if (source->debug != MagickFalse || destination->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s", - source->filename,destination->filename); - - /* - Overlay single source image over destation image/list - */ - if (source->next == (Image *) NULL) - while (destination != (Image *) NULL) - { - CompositeCanvas(destination, compose, source, x_offset, y_offset, - exception); - destination=GetNextImageInList(destination); - } - - /* - Overlay source image list over single destination. - Multiple clones of destination image are created to match source list. - Original Destination image becomes first image of generated list. - As such the image list pointer does not require any change in caller. - Some animation attributes however also needs coping in this case. - */ - else if (destination->next == (Image *) NULL) - { - Image *dest = CloneImage(destination,0,0,MagickTrue,exception); - - CompositeCanvas(destination, compose, source, x_offset, y_offset, - exception); - /* copy source image attributes ? */ - if (source->next != (Image *) NULL) - { - destination->delay = source->delay; - destination->iterations = source->iterations; - } - source=GetNextImageInList(source); - - while (source != (Image *) NULL) - { - AppendImageToList(&destination, - CloneImage(dest,0,0,MagickTrue,exception)); - destination=GetLastImageInList(destination); - - CompositeCanvas(destination, compose, source, x_offset, y_offset, - exception); - destination->delay = source->delay; - destination->iterations = source->iterations; - source=GetNextImageInList(source); - } - dest=DestroyImage(dest); - } - - /* - Overlay a source image list over a destination image list - until either list runs out of images. (Does not repeat) - */ - else - while (source != (Image *) NULL && destination != (Image *) NULL) - { - CompositeCanvas(destination, compose, source, x_offset, y_offset, - exception); - source=GetNextImageInList(source); - destination=GetNextImageInList(destination); - } -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % M e r g e I m a g e L a y e r s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % MergeImageLayers() composes all the image layers from the current given - % image onward to produce a single image of the merged layers. - % - % The inital canvas's size depends on the given LayerMethod, and is - % initialized using the first images background color. The images - % are then compositied onto that image in sequence using the given - % composition that has been assigned to each individual image. - % - % The format of the MergeImageLayers is: - % - % Image *MergeImageLayers(Image *image,const LayerMethod method, - % ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image list to be composited together - % - % o method: the method of selecting the size of the initial canvas. - % - % MergeLayer: Merge all layers onto a canvas just large enough - % to hold all the actual images. The virtual canvas of the - % first image is preserved but otherwise ignored. - % - % FlattenLayer: Use the virtual canvas size of first image. - % Images which fall outside this canvas is clipped. - % This can be used to 'fill out' a given virtual canvas. - % - % MosaicLayer: Start with the virtual canvas of the first image, - % enlarging left and right edges to contain all images. - % Images with negative offsets will be clipped. - % - % TrimBoundsLayer: Determine the overall bounds of all the image - % layers just as in "MergeLayer", then adjust the the canvas - % and offsets to be relative to those bounds, without overlaying - % the images. - % - % WARNING: a new image is not returned, the original image - % sequence page data is modified instead. - % - % o exception: return any errors or warnings in this structure. - % - */ -MagickExport Image *MergeImageLayers(Image *image,const LayerMethod method, - ExceptionInfo *exception) -{ -#define MergeLayersTag "Merge/Layers" - - Image - *canvas; - - MagickBooleanType - proceed; - - RectangleInfo - page; - - register const Image - *next; - - size_t - number_images, - height, - width; - - ssize_t - scene; - - assert(image != (Image *) NULL); - assert(image->signature == MagickCoreSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - /* - Determine canvas image size, and its virtual canvas size and offset - */ - page=image->page; - width=image->columns; - height=image->rows; - switch (method) - { - case TrimBoundsLayer: - case MergeLayer: - default: - { - next=GetNextImageInList(image); - for ( ; next != (Image *) NULL; next=GetNextImageInList(next)) - { - if (page.x > next->page.x) - { - width+=page.x-next->page.x; - page.x=next->page.x; - } - if (page.y > next->page.y) - { - height+=page.y-next->page.y; - page.y=next->page.y; - } - if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x)) - width=(size_t) next->page.x+(ssize_t) next->columns-page.x; - if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y)) - height=(size_t) next->page.y+(ssize_t) next->rows-page.y; - } - break; - } - case FlattenLayer: - { - if (page.width > 0) - width=page.width; - if (page.height > 0) - height=page.height; - page.x=0; - page.y=0; - break; - } - case MosaicLayer: - { - if (page.width > 0) - width=page.width; - if (page.height > 0) - height=page.height; - for (next=image; next != (Image *) NULL; next=GetNextImageInList(next)) - { - if (method == MosaicLayer) - { - page.x=next->page.x; - page.y=next->page.y; - if ((ssize_t) width < (next->page.x+(ssize_t) next->columns)) - width=(size_t) next->page.x+next->columns; - if ((ssize_t) height < (next->page.y+(ssize_t) next->rows)) - height=(size_t) next->page.y+next->rows; - } - } - page.width=width; - page.height=height; - page.x=0; - page.y=0; - } - break; - } - /* - Set virtual canvas size if not defined. - */ - if (page.width == 0) - page.width=page.x < 0 ? width : width+page.x; - if (page.height == 0) - page.height=page.y < 0 ? height : height+page.y; - /* - Handle "TrimBoundsLayer" method separately to normal 'layer merge'. - */ - if (method == TrimBoundsLayer) - { - number_images=GetImageListLength(image); - for (scene=0; scene < (ssize_t) number_images; scene++) - { - image->page.x-=page.x; - image->page.y-=page.y; - image->page.width=width; - image->page.height=height; - proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene, - number_images); - if (proceed == MagickFalse) - break; - image=GetNextImageInList(image); - if (image == (Image *) NULL) - break; - } - return((Image *) NULL); - } - /* - Create canvas size of width and height, and background color. - */ - canvas=CloneImage(image,width,height,MagickTrue,exception); - if (canvas == (Image *) NULL) - return((Image *) NULL); - (void) SetImageBackgroundColor(canvas,exception); - canvas->page=page; - canvas->dispose=UndefinedDispose; - /* - Compose images onto canvas, with progress monitor - */ - number_images=GetImageListLength(image); - for (scene=0; scene < (ssize_t) number_images; scene++) - { - (void) CompositeImage(canvas,image,image->compose,MagickTrue,image->page.x- - canvas->page.x,image->page.y-canvas->page.y,exception); - proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene, - number_images); - if (proceed == MagickFalse) - break; - image=GetNextImageInList(image); - if (image == (Image *) NULL) - break; - } - return(canvas); -} - diff --git a/test/bug-hunting/cve/CVE-2019-14249/README b/test/bug-hunting/cve/CVE-2019-14249/README deleted file mode 100644 index e67ed772ca7..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14249/README +++ /dev/null @@ -1,10 +0,0 @@ - - -Division by zero - -Details: -https://nvd.nist.gov/vuln/detail/CVE-2019-14249 - -Fix: -https://sourceforge.net/p/libdwarf/code/ci/cb7198abde46c2ae29957ad460da6886eaa606ba/tree/libdwarf/dwarf_elf_load_headers.c?diff=99e77c3894877a1dd80b82808d8309eded4e5599 - diff --git a/test/bug-hunting/cve/CVE-2019-14249/dwarf_elf_load_headers.c b/test/bug-hunting/cve/CVE-2019-14249/dwarf_elf_load_headers.c deleted file mode 100644 index 12e360d52c9..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14249/dwarf_elf_load_headers.c +++ /dev/null @@ -1,2115 +0,0 @@ -/* - Copyright 2018 David Anderson. All rights reserved. - - Redistribution and use in source and binary forms, with - or without modification, are permitted provided that the - following conditions are met: - - Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - - 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 COPYRIGHT HOLDERS AND - CONTRIBUTORS "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 COPYRIGHT HOLDER OR - CONTRIBUTORS 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. - */ - -/* This reads elf headers and creates generic-elf - structures containing the Elf headers. */ - - -#include "config.h" -#include -#include /* For memcpy etc */ -#include -#include -#include /* for open() */ -#include /* for open() */ -#include /* for open() */ -#ifdef HAVE_UNISTD_H -#include /* lseek read close */ -#elif defined(_WIN32) && defined(_MSC_VER) -#include -#endif /* HAVE_UNISTD_H */ - -/* Windows specific header files */ -#if defined(_WIN32) && defined(HAVE_STDAFX_H) -#include "stdafx.h" -#endif /* HAVE_STDAFX_H */ - -#include "libdwarfdefs.h" -#include "dwarf.h" -#include "libdwarf.h" -#include "dwarf_base_types.h" -#include "dwarf_opaque.h" -#include "memcpy_swap.h" -#include "dwarf_elfstructs.h" -#include "dwarf_reading.h" -#include "dwarf_elf_defines.h" -#include "dwarf_elfread.h" -#include "dwarf_object_detector.h" -#include "dwarf_object_read_common.h" -#include "dwarf_util.h" - -#ifndef O_BINARY -#define O_BINARY 0 -#endif /* O_BINARY */ - -#ifdef HAVE_UNUSED_ATTRIBUTE -#define UNUSEDARG __attribute__ ((unused)) -#else -#define UNUSEDARG -#endif -#define TRUE 1 -#define FALSE 0 - -#ifdef WORDS_BIGENDIAN -#define ASNAR(func,t,s) \ - do { \ - unsigned tbyte = sizeof(t) - sizeof(s); \ - t = 0; \ - func(((char *)&t)+tbyte,&s[0],sizeof(s)); \ - } while (0) -#else /* LITTLE ENDIAN */ -#define ASNAR(func,t,s) \ - do { \ - t = 0; \ - func(&t,&s[0],sizeof(s)); \ - } while (0) -#endif /* end LITTLE- BIG-ENDIAN */ - -static int -_dwarf_load_elf_section_is_dwarf(const char *sname) -{ - if (!strncmp(sname,".rel",4)) { - return FALSE; - } - if (!strncmp(sname,".debug_",7)) { - return TRUE; - } - if (!strncmp(sname,".zdebug_",8)) { - return TRUE; - } - if (!strcmp(sname,".eh_frame")) { - return TRUE; - } - if (!strncmp(sname,".gdb_index",10)) { - return TRUE; - } - return FALSE; -} - - -static int -is_empty_section(Dwarf_Unsigned type) -{ - if (type == SHT_NOBITS) { - return TRUE; - } - if (type == SHT_NULL) { - return TRUE; - } - return FALSE; -} - -#if 0 -int -dwarf_construct_elf_access_path(const char *path, - dwarf_elf_object_access_internals_t **mp,int *errcode) -{ - int fd = -1; - int res = 0; - dwarf_elf_object_access_internals_t *mymp = 0; - - fd = open(path, O_RDONLY|O_BINARY); - if (fd < 0) { - *errcode = DW_DLE_PATH_SIZE_TOO_SMALL; - return DW_DLV_ERROR; - } - res = dwarf_construct_elf_access(fd, - path,&mymp,errcode); - if (res != DW_DLV_OK) { - close(fd); - return res; - } - mymp->f_destruct_close_fd = TRUE; - *mp = mymp; - return res; -} -#endif /* 0 */ - -/* Here path is not essential. Pass in with "" if unknown. */ -int -dwarf_construct_elf_access(int fd, - const char *path, - dwarf_elf_object_access_internals_t **mp,int *errcode) -{ - unsigned ftype = 0; - unsigned endian = 0; - unsigned offsetsize = 0; - Dwarf_Unsigned filesize = 0; - dwarf_elf_object_access_internals_t *mfp = 0; - int res = 0; - - res = dwarf_object_detector_fd(fd, - &ftype,&endian,&offsetsize, &filesize, errcode); - if (res != DW_DLV_OK) { - return res; - } - - mfp = calloc(1,sizeof(dwarf_elf_object_access_internals_t)); - if (!mfp) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - /* For non-libelf Elf, call it 'F'. Libelf Elf uses 'E' */ - mfp->f_ident[0] = 'F'; - mfp->f_ident[1] = 1; - mfp->f_fd = fd; - mfp->f_destruct_close_fd = FALSE; - mfp->f_is_64bit = ((offsetsize==64)?TRUE:FALSE); - mfp->f_filesize = filesize; - mfp->f_offsetsize = offsetsize; - mfp->f_pointersize = offsetsize; - mfp->f_endian = endian; - mfp->f_ftype = ftype; - mfp->f_path = strdup(path); - - *mp = mfp; - return DW_DLV_OK; -} - -/* Caller must zero the passed in pointer - after this returns to remind - the caller to avoid use of the pointer. */ -int -dwarf_destruct_elf_access(dwarf_elf_object_access_internals_t* ep, - UNUSEDARG int *errcode) -{ - struct generic_shdr *shp = 0; - Dwarf_Unsigned shcount = 0; - Dwarf_Unsigned i = 0; - - free(ep->f_ehdr); - shp = ep->f_shdr; - shcount = ep->f_loc_shdr.g_count; - for (i = 0; i < shcount; ++i,++shp) { - free(shp->gh_rels); - shp->gh_rels = 0; - free(shp->gh_content); - shp->gh_content = 0; - free(shp->gh_sht_group_array); - shp->gh_sht_group_array = 0; - shp->gh_sht_group_array_count = 0; - } - free(ep->f_shdr); - free(ep->f_phdr); - free(ep->f_elf_shstrings_data); - free(ep->f_dynamic); - free(ep->f_symtab_sect_strings); - free(ep->f_dynsym_sect_strings); - free(ep->f_symtab); - free(ep->f_dynsym); - - /* if TRUE close f_fd on destruct.*/ - if (ep->f_destruct_close_fd) { - close(ep->f_fd); - } - ep->f_ident[0] = 'X'; - free(ep->f_path); - free(ep); - return DW_DLV_OK; -} - - - - -static int -generic_ehdr_from_32(dwarf_elf_object_access_internals_t *ep, - struct generic_ehdr *ehdr, dw_elf32_ehdr *e, - UNUSEDARG int *errcode) -{ - int i = 0; - - for (i = 0; i < EI_NIDENT; ++i) { - ehdr->ge_ident[i] = e->e_ident[i]; - } - ASNAR(ep->f_copy_word,ehdr->ge_type,e->e_type); - ASNAR(ep->f_copy_word,ehdr->ge_machine,e->e_machine); - ASNAR(ep->f_copy_word,ehdr->ge_version,e->e_version); - ASNAR(ep->f_copy_word,ehdr->ge_entry,e->e_entry); - ASNAR(ep->f_copy_word,ehdr->ge_phoff,e->e_phoff); - ASNAR(ep->f_copy_word,ehdr->ge_shoff,e->e_shoff); - ASNAR(ep->f_copy_word,ehdr->ge_flags,e->e_flags); - ASNAR(ep->f_copy_word,ehdr->ge_ehsize,e->e_ehsize); - ASNAR(ep->f_copy_word,ehdr->ge_phentsize,e->e_phentsize); - ASNAR(ep->f_copy_word,ehdr->ge_phnum,e->e_phnum); - ASNAR(ep->f_copy_word,ehdr->ge_shentsize,e->e_shentsize); - ASNAR(ep->f_copy_word,ehdr->ge_shnum,e->e_shnum); - ASNAR(ep->f_copy_word,ehdr->ge_shstrndx,e->e_shstrndx); - ep->f_machine = ehdr->ge_machine; - ep->f_ehdr = ehdr; - ep->f_loc_ehdr.g_name = "Elf File Header"; - ep->f_loc_ehdr.g_offset = 0; - ep->f_loc_ehdr.g_count = 1; - ep->f_loc_ehdr.g_entrysize = sizeof(dw_elf32_ehdr); - ep->f_loc_ehdr.g_totalsize = sizeof(dw_elf32_ehdr); - return DW_DLV_OK; -} - -static int -generic_ehdr_from_64(dwarf_elf_object_access_internals_t* ep, - struct generic_ehdr *ehdr, dw_elf64_ehdr *e, - UNUSEDARG int *errcode) -{ - int i = 0; - - for (i = 0; i < EI_NIDENT; ++i) { - ehdr->ge_ident[i] = e->e_ident[i]; - } - ASNAR(ep->f_copy_word,ehdr->ge_type,e->e_type); - ASNAR(ep->f_copy_word,ehdr->ge_machine,e->e_machine); - ASNAR(ep->f_copy_word,ehdr->ge_version,e->e_version); - ASNAR(ep->f_copy_word,ehdr->ge_entry,e->e_entry); - ASNAR(ep->f_copy_word,ehdr->ge_phoff,e->e_phoff); - ASNAR(ep->f_copy_word,ehdr->ge_shoff,e->e_shoff); - ASNAR(ep->f_copy_word,ehdr->ge_flags,e->e_flags); - ASNAR(ep->f_copy_word,ehdr->ge_ehsize,e->e_ehsize); - ASNAR(ep->f_copy_word,ehdr->ge_phentsize,e->e_phentsize); - ASNAR(ep->f_copy_word,ehdr->ge_phnum,e->e_phnum); - ASNAR(ep->f_copy_word,ehdr->ge_shentsize,e->e_shentsize); - ASNAR(ep->f_copy_word,ehdr->ge_shnum,e->e_shnum); - ASNAR(ep->f_copy_word,ehdr->ge_shstrndx,e->e_shstrndx); - ep->f_machine = ehdr->ge_machine; - ep->f_ehdr = ehdr; - ep->f_loc_ehdr.g_name = "Elf File Header"; - ep->f_loc_ehdr.g_offset = 0; - ep->f_loc_ehdr.g_count = 1; - ep->f_loc_ehdr.g_entrysize = sizeof(dw_elf64_ehdr); - ep->f_loc_ehdr.g_totalsize = sizeof(dw_elf64_ehdr); - return DW_DLV_OK; -} - - -#if 0 /* not used */ -static int -generic_phdr_from_phdr32(dwarf_elf_object_access_internals_t* ep, - struct generic_phdr **phdr_out, - Dwarf_Unsigned * count_out, - Dwarf_Unsigned offset, - Dwarf_Unsigned entsize, - Dwarf_Unsigned count, - int *errcode) -{ - dw_elf32_phdr *pph =0; - dw_elf32_phdr *orig_pph =0; - struct generic_phdr *gphdr =0; - struct generic_phdr *orig_gphdr =0; - Dwarf_Unsigned i = 0; - int res = 0; - - *count_out = 0; - pph = (dw_elf32_phdr *)calloc(count, entsize); - if (pph == 0) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - gphdr = (struct generic_phdr *)calloc(count,sizeof(*gphdr)); - if (gphdr == 0) { - free(pph); - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - - orig_pph = pph; - orig_gphdr = gphdr; - res = RRMOA(ep->f_fd,pph,offset,count*entsize, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - free(pph); - free(gphdr); - return res; - } - for (i = 0; i < count; - ++i, pph++,gphdr++) { - ASNAR(ep->f_copy_word,gphdr->gp_type,pph->p_type); - ASNAR(ep->f_copy_word,gphdr->gp_offset,pph->p_offset); - ASNAR(ep->f_copy_word,gphdr->gp_vaddr,pph->p_vaddr); - ASNAR(ep->f_copy_word,gphdr->gp_paddr,pph->p_paddr); - ASNAR(ep->f_copy_word,gphdr->gp_filesz,pph->p_filesz); - ASNAR(ep->f_copy_word,gphdr->gp_memsz,pph->p_memsz); - ASNAR(ep->f_copy_word,gphdr->gp_flags,pph->p_flags); - ASNAR(ep->f_copy_word,gphdr->gp_align,pph->p_align); - } - free(orig_pph); - *phdr_out = orig_gphdr; - *count_out = count; - ep->f_phdr = orig_gphdr; - ep->f_loc_phdr.g_name = "Program Header"; - ep->f_loc_phdr.g_offset = offset; - ep->f_loc_phdr.g_count = count; - ep->f_loc_phdr.g_entrysize = sizeof(dw_elf32_phdr); - ep->f_loc_phdr.g_totalsize = sizeof(dw_elf32_phdr)*count; - return DW_DLV_OK; -} - -static int -generic_phdr_from_phdr64(dwarf_elf_object_access_internals_t* ep, - struct generic_phdr **phdr_out, - Dwarf_Unsigned * count_out, - Dwarf_Unsigned offset, - Dwarf_Unsigned entsize, - Dwarf_Unsigned count, - int *errcode) -{ - dw_elf64_phdr *pph =0; - dw_elf64_phdr *orig_pph =0; - struct generic_phdr *gphdr =0; - struct generic_phdr *orig_gphdr =0; - int res = 0; - Dwarf_Unsigned i = 0; - - *count_out = 0; - pph = (dw_elf64_phdr *)calloc(count, entsize); - if (pph == 0) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - gphdr = (struct generic_phdr *)calloc(count,sizeof(*gphdr)); - if (gphdr == 0) { - free(pph); - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - - orig_pph = pph; - orig_gphdr = gphdr; - res = RRMOA(ep->f_fd,pph,offset,count*entsize, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - free(pph); - free(gphdr); - return res; - } - for (i = 0; i < count; - ++i, pph++,gphdr++) { - ASNAR(ep->f_copy_word,gphdr->gp_type,pph->p_type); - ASNAR(ep->f_copy_word,gphdr->gp_offset,pph->p_offset); - ASNAR(ep->f_copy_word,gphdr->gp_vaddr,pph->p_vaddr); - ASNAR(ep->f_copy_word,gphdr->gp_paddr,pph->p_paddr); - ASNAR(ep->f_copy_word,gphdr->gp_filesz,pph->p_filesz); - ASNAR(ep->f_copy_word,gphdr->gp_memsz,pph->p_memsz); - ASNAR(ep->f_copy_word,gphdr->gp_flags,pph->p_flags); - ASNAR(ep->f_copy_word,gphdr->gp_align,pph->p_align); - } - free(orig_pph); - *phdr_out = orig_gphdr; - *count_out = count; - ep->f_phdr = orig_gphdr; - ep->f_loc_phdr.g_name = "Program Header"; - ep->f_loc_phdr.g_offset = offset; - ep->f_loc_phdr.g_count = count; - ep->f_loc_phdr.g_entrysize = sizeof(dw_elf64_phdr); - ep->f_loc_phdr.g_totalsize = sizeof(dw_elf64_phdr)*count; - return DW_DLV_OK; -} -#endif /* not used */ - -static int -generic_shdr_from_shdr32(dwarf_elf_object_access_internals_t *ep, - Dwarf_Unsigned * count_out, - Dwarf_Unsigned offset, - Dwarf_Unsigned entsize, - Dwarf_Unsigned count, - int *errcode) -{ - dw_elf32_shdr *psh =0; - dw_elf32_shdr *orig_psh =0; - struct generic_shdr *gshdr =0; - struct generic_shdr *orig_gshdr =0; - Dwarf_Unsigned i = 0; - int res = 0; - - *count_out = 0; - psh = (dw_elf32_shdr *)calloc(count, entsize); - if (!psh) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - gshdr = (struct generic_shdr *)calloc(count,sizeof(*gshdr)); - if (!gshdr) { - free(psh); - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - - orig_psh = psh; - orig_gshdr = gshdr; - res = RRMOA(ep->f_fd,psh,offset,count*entsize, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - free(psh); - free(gshdr); - return res; - } - for (i = 0; i < count; - ++i, psh++,gshdr++) { - gshdr->gh_secnum = i; - ASNAR(ep->f_copy_word,gshdr->gh_name,psh->sh_name); - ASNAR(ep->f_copy_word,gshdr->gh_type,psh->sh_type); - ASNAR(ep->f_copy_word,gshdr->gh_flags,psh->sh_flags); - ASNAR(ep->f_copy_word,gshdr->gh_addr,psh->sh_addr); - ASNAR(ep->f_copy_word,gshdr->gh_offset,psh->sh_offset); - ASNAR(ep->f_copy_word,gshdr->gh_size,psh->sh_size); - ASNAR(ep->f_copy_word,gshdr->gh_link,psh->sh_link); - ASNAR(ep->f_copy_word,gshdr->gh_info,psh->sh_info); - ASNAR(ep->f_copy_word,gshdr->gh_addralign,psh->sh_addralign); - ASNAR(ep->f_copy_word,gshdr->gh_entsize,psh->sh_entsize); - if (gshdr->gh_type == SHT_REL || gshdr->gh_type == SHT_RELA) { - gshdr->gh_reloc_target_secnum = gshdr->gh_info; - } - } - free(orig_psh); - *count_out = count; - ep->f_shdr = orig_gshdr; - ep->f_loc_shdr.g_name = "Section Header"; - ep->f_loc_shdr.g_count = count; - ep->f_loc_shdr.g_offset = offset; - ep->f_loc_shdr.g_entrysize = sizeof(dw_elf32_shdr); - ep->f_loc_shdr.g_totalsize = sizeof(dw_elf32_shdr)*count; - return DW_DLV_OK; -} - -static int -generic_shdr_from_shdr64(dwarf_elf_object_access_internals_t *ep, - Dwarf_Unsigned * count_out, - Dwarf_Unsigned offset, - Dwarf_Unsigned entsize, - Dwarf_Unsigned count, - int *errcode) -{ - dw_elf64_shdr *psh =0; - dw_elf64_shdr *orig_psh =0; - struct generic_shdr *gshdr =0; - struct generic_shdr *orig_gshdr =0; - Dwarf_Unsigned i = 0; - int res = 0; - - *count_out = 0; - psh = (dw_elf64_shdr *)calloc(count, entsize); - if (!psh) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - gshdr = (struct generic_shdr *)calloc(count,sizeof(*gshdr)); - if (gshdr == 0) { - free(psh); - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - - orig_psh = psh; - orig_gshdr = gshdr; - res = RRMOA(ep->f_fd,psh,offset,count*entsize, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - free(psh); - free(gshdr); - return res; - } - for (i = 0; i < count; - ++i, psh++,gshdr++) { - gshdr->gh_secnum = i; - ASNAR(ep->f_copy_word,gshdr->gh_name,psh->sh_name); - ASNAR(ep->f_copy_word,gshdr->gh_type,psh->sh_type); - ASNAR(ep->f_copy_word,gshdr->gh_flags,psh->sh_flags); - ASNAR(ep->f_copy_word,gshdr->gh_addr,psh->sh_addr); - ASNAR(ep->f_copy_word,gshdr->gh_offset,psh->sh_offset); - ASNAR(ep->f_copy_word,gshdr->gh_size,psh->sh_size); - ASNAR(ep->f_copy_word,gshdr->gh_link,psh->sh_link); - ASNAR(ep->f_copy_word,gshdr->gh_info,psh->sh_info); - ASNAR(ep->f_copy_word,gshdr->gh_addralign,psh->sh_addralign); - ASNAR(ep->f_copy_word,gshdr->gh_entsize,psh->sh_entsize); - if (gshdr->gh_type == SHT_REL || gshdr->gh_type == SHT_RELA) { - gshdr->gh_reloc_target_secnum = gshdr->gh_info; - } - } - free(orig_psh); - *count_out = count; - ep->f_shdr = orig_gshdr; - ep->f_loc_shdr.g_name = "Section Header"; - ep->f_loc_shdr.g_count = count; - ep->f_loc_shdr.g_offset = offset; - ep->f_loc_shdr.g_entrysize = sizeof(dw_elf64_shdr); - ep->f_loc_shdr.g_totalsize = sizeof(dw_elf64_shdr)*count; - return DW_DLV_OK; -} - - - -static int -dwarf_generic_elf_load_symbols32( - dwarf_elf_object_access_internals_t *ep, - struct generic_symentry **gsym_out, - Dwarf_Unsigned offset,Dwarf_Unsigned size, - Dwarf_Unsigned *count_out,int *errcode) -{ - Dwarf_Unsigned ecount = 0; - Dwarf_Unsigned size2 = 0; - Dwarf_Unsigned i = 0; - dw_elf32_sym *psym = 0; - dw_elf32_sym *orig_psym = 0; - struct generic_symentry * gsym = 0; - struct generic_symentry * orig_gsym = 0; - int res = 0; - - ecount = (long)(size/sizeof(dw_elf32_sym)); - size2 = ecount * sizeof(dw_elf32_sym); - if (size != size2) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - psym = calloc(ecount,sizeof(dw_elf32_sym)); - if (!psym) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - gsym = calloc(ecount,sizeof(struct generic_symentry)); - if (!gsym) { - free(psym); - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = RRMOA(ep->f_fd,psym,offset,size, - ep->f_filesize,errcode); - if (res!= DW_DLV_OK) { - free(psym); - free(gsym); - return res; - } - orig_psym = psym; - orig_gsym = gsym; - for (i = 0; i < ecount; ++i,++psym,++gsym) { - Dwarf_Unsigned bind = 0; - Dwarf_Unsigned type = 0; - - ASNAR(ep->f_copy_word,gsym->gs_name,psym->st_name); - ASNAR(ep->f_copy_word,gsym->gs_value,psym->st_value); - ASNAR(ep->f_copy_word,gsym->gs_size,psym->st_size); - ASNAR(ep->f_copy_word,gsym->gs_info,psym->st_info); - ASNAR(ep->f_copy_word,gsym->gs_other,psym->st_other); - ASNAR(ep->f_copy_word,gsym->gs_shndx,psym->st_shndx); - bind = gsym->gs_info >> 4; - type = gsym->gs_info & 0xf; - gsym->gs_bind = bind; - gsym->gs_type = type; - } - *count_out = ecount; - *gsym_out = orig_gsym; - free(orig_psym); - return DW_DLV_OK; -} - - -static int -dwarf_generic_elf_load_symbols64( - dwarf_elf_object_access_internals_t *ep, - struct generic_symentry **gsym_out, - Dwarf_Unsigned offset,Dwarf_Unsigned size, - Dwarf_Unsigned *count_out,int *errcode) -{ - Dwarf_Unsigned ecount = 0; - Dwarf_Unsigned size2 = 0; - Dwarf_Unsigned i = 0; - dw_elf64_sym *psym = 0; - dw_elf64_sym *orig_psym = 0; - struct generic_symentry * gsym = 0; - struct generic_symentry * orig_gsym = 0; - int res = 0; - - ecount = (long)(size/sizeof(dw_elf64_sym)); - size2 = ecount * sizeof(dw_elf64_sym); - if (size != size2) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - psym = calloc(ecount,sizeof(dw_elf64_sym)); - if (!psym) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - gsym = calloc(ecount,sizeof(struct generic_symentry)); - if (!gsym) { - free(psym); - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = RRMOA(ep->f_fd,psym,offset,size, - ep->f_filesize,errcode); - if (res!= DW_DLV_OK) { - free(psym); - free(gsym); - *errcode = DW_DLE_ALLOC_FAIL; - return res; - } - orig_psym = psym; - orig_gsym = gsym; - for (i = 0; i < ecount; ++i,++psym,++gsym) { - Dwarf_Unsigned bind = 0; - Dwarf_Unsigned type = 0; - - ASNAR(ep->f_copy_word,gsym->gs_name,psym->st_name); - ASNAR(ep->f_copy_word,gsym->gs_value,psym->st_value); - ASNAR(ep->f_copy_word,gsym->gs_size,psym->st_size); - ASNAR(ep->f_copy_word,gsym->gs_info,psym->st_info); - ASNAR(ep->f_copy_word,gsym->gs_other,psym->st_other); - ASNAR(ep->f_copy_word,gsym->gs_shndx,psym->st_shndx); - bind = gsym->gs_info >> 4; - type = gsym->gs_info & 0xf; - gsym->gs_bind = bind; - gsym->gs_type = type; - } - *count_out = ecount; - *gsym_out = orig_gsym; - free(orig_psym); - return DW_DLV_OK; -} - -static int -dwarf_generic_elf_load_symbols( - dwarf_elf_object_access_internals_t *ep, - int secnum, - struct generic_shdr *psh, - struct generic_symentry **gsym_out, - Dwarf_Unsigned *count_out,int *errcode) -{ - int res = 0; - struct generic_symentry *gsym = 0; - Dwarf_Unsigned count = 0; - - if (!secnum) { - return DW_DLV_NO_ENTRY; - } - if (ep->f_offsetsize == 32) { - res = dwarf_generic_elf_load_symbols32(ep, - &gsym, - psh->gh_offset,psh->gh_size, - &count,errcode); - } else if (ep->f_offsetsize == 64) { - res = dwarf_generic_elf_load_symbols64(ep, - &gsym, - psh->gh_offset,psh->gh_size, - &count,errcode); - } else { - *errcode = DW_DLE_OFFSET_SIZE; - return DW_DLV_ERROR; - } - if (res == DW_DLV_OK) { - *gsym_out = gsym; - *count_out = count; - } - return res; -} -#if 0 -int -dwarf_load_elf_dynsym_symbols( - dwarf_elf_object_access_internals_t *ep, int*errcode) -{ - int res = 0; - struct generic_symentry *gsym = 0; - Dwarf_Unsigned count = 0; - Dwarf_Unsigned secnum = ep->f_dynsym_sect_index; - struct generic_shdr * psh = 0; - - if (!secnum) { - return DW_DLV_NO_ENTRY; - } - psh = ep->f_shdr + secnum; - res = dwarf_generic_elf_load_symbols(ep, - secnum, - psh, - &gsym, - &count,errcode); - if (res == DW_DLV_OK) { - ep->f_dynsym = gsym; - ep->f_loc_dynsym.g_count = count; - } - return res; -} -#endif /* 0 */ - -int -_dwarf_load_elf_symtab_symbols( - dwarf_elf_object_access_internals_t *ep, int*errcode) -{ - int res = 0; - struct generic_symentry *gsym = 0; - Dwarf_Unsigned count = 0; - Dwarf_Unsigned secnum = ep->f_symtab_sect_index; - struct generic_shdr * psh = 0; - - if (!secnum) { - return DW_DLV_NO_ENTRY; - } - psh = ep->f_shdr + secnum; - res = dwarf_generic_elf_load_symbols(ep, - secnum, - psh, - &gsym, - &count,errcode); - if (res == DW_DLV_OK) { - ep->f_symtab = gsym; - ep->f_loc_symtab.g_count = count; - } - return res; -} - -static int -generic_rel_from_rela32( - dwarf_elf_object_access_internals_t *ep, - struct generic_shdr * gsh, - dw_elf32_rela *relp, - struct generic_rela *grel, - int *errcode) -{ - Dwarf_Unsigned ecount = 0; - Dwarf_Unsigned size = gsh->gh_size; - Dwarf_Unsigned size2 = 0; - Dwarf_Unsigned i = 0; - - ecount = size/sizeof(dw_elf32_rela); - size2 = ecount * sizeof(dw_elf32_rela); - if (size != size2) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - for (i = 0; i < ecount; ++i,++relp,++grel) { - ASNAR(ep->f_copy_word,grel->gr_offset,relp->r_offset); - ASNAR(ep->f_copy_word,grel->gr_info,relp->r_info); - /* addend signed */ - ASNAR(ep->f_copy_word,grel->gr_addend,relp->r_addend); - SIGN_EXTEND(grel->gr_addend,sizeof(relp->r_addend)); - grel->gr_isrela = TRUE; - grel->gr_sym = grel->gr_info>>8; /* ELF32_R_SYM */ - grel->gr_type = grel->gr_info & 0xff; - } - return DW_DLV_OK; -} - -static int -generic_rel_from_rela64( - dwarf_elf_object_access_internals_t *ep, - struct generic_shdr * gsh, - dw_elf64_rela *relp, - struct generic_rela *grel, int *errcode) -{ - Dwarf_Unsigned ecount = 0; - Dwarf_Unsigned size = gsh->gh_size; - Dwarf_Unsigned size2 = 0; - Dwarf_Unsigned i = 0; - int objlittleendian = (ep->f_endian == DW_OBJECT_LSB); - int ismips64 = (ep->f_machine == EM_MIPS); - int issparcv9 = (ep->f_machine == EM_SPARCV9); - - ecount = size/sizeof(dw_elf64_rela); - size2 = ecount * sizeof(dw_elf64_rela); - if (size != size2) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - for (i = 0; i < ecount; ++i,++relp,++grel) { - ASNAR(ep->f_copy_word,grel->gr_offset,relp->r_offset); - ASNAR(ep->f_copy_word,grel->gr_info,relp->r_info); - ASNAR(ep->f_copy_word,grel->gr_addend,relp->r_addend); - SIGN_EXTEND(grel->gr_addend,sizeof(relp->r_addend)); - if (ismips64 && objlittleendian) { - char realsym[4]; - - memcpy(realsym,&relp->r_info,sizeof(realsym)); - ASNAR(ep->f_copy_word,grel->gr_sym,realsym); - grel->gr_type = relp->r_info[7]; - grel->gr_type2 = relp->r_info[6]; - grel->gr_type3 = relp->r_info[5]; - } else if (issparcv9) { - /* Always Big Endian? */ - char realsym[4]; - - memcpy(realsym,&relp->r_info,sizeof(realsym)); - ASNAR(ep->f_copy_word,grel->gr_sym,realsym); - grel->gr_type = relp->r_info[7]; - } else { - grel->gr_sym = grel->gr_info >> 32; - grel->gr_type = grel->gr_info & 0xffffffff; - } - grel->gr_isrela = TRUE; - } - return DW_DLV_OK; -} - -#if 0 -static int -generic_rel_from_rel32( - dwarf_elf_object_access_internals_t *ep, - struct generic_shdr * gsh, - dw_elf32_rel *relp, - struct generic_rela *grel,int *errcode) -{ - Dwarf_Unsigned ecount = 0; - Dwarf_Unsigned size = gsh->gh_size; - Dwarf_Unsigned size2 = 0; - Dwarf_Unsigned i = 0; - - ecount = size/sizeof(dw_elf32_rel); - size2 = ecount * sizeof(dw_elf32_rel); - if (size != size2) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - for (i = 0; i < ecount; ++i,++relp,++grel) { - grel->gr_isrela = 0; - ASNAR(ep->f_copy_word,grel->gr_offset,relp->r_offset); - ASNAR(ep->f_copy_word,grel->gr_info,relp->r_info); - grel->gr_addend = 0; /* Unused for plain .rel */ - grel->gr_sym = grel->gr_info >>8; /* ELF32_R_SYM */ - grel->gr_isrela = FALSE; - grel->gr_type = grel->gr_info & 0xff; - } - return DW_DLV_OK; -} -#endif /* 0 */ - -#if 0 -static int -generic_rel_from_rel64( - dwarf_elf_object_access_internals_t *ep, - struct generic_shdr * gsh, - dw_elf64_rel *relp, - struct generic_rela *grel,int *errcode) -{ - Dwarf_Unsigned ecount = 0; - Dwarf_Unsigned size = gsh->gh_size; - Dwarf_Unsigned size2 = 0; - Dwarf_Unsigned i = 0; - int objlittleendian = (ep->f_endian == DW_OBJECT_LSB); - int ismips64 = (ep->f_machine == EM_MIPS); - int issparcv9 = (ep->f_machine == EM_SPARCV9); - - ecount = size/sizeof(dw_elf64_rel); - size2 = ecount * sizeof(dw_elf64_rel); - if (size != size2) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - for (i = 0; i < ecount; ++i,++relp,++grel) { - grel->gr_isrela = 0; - ASNAR(ep->f_copy_word,grel->gr_offset,relp->r_offset); - ASNAR(ep->f_copy_word,grel->gr_info,relp->r_info); - grel->gr_addend = 0; /* Unused for plain .rel */ - if (ismips64 && objlittleendian) { - char realsym[4]; - - memcpy(realsym,&relp->r_info,sizeof(realsym)); - ASNAR(ep->f_copy_word,grel->gr_sym,realsym); - grel->gr_type = relp->r_info[7]; - grel->gr_type2 = relp->r_info[6]; - grel->gr_type3 = relp->r_info[5]; - } else if (issparcv9) { - /* Always Big Endian? */ - char realsym[4]; - - memcpy(realsym,&relp->r_info,sizeof(realsym)); - ASNAR(ep->f_copy_word,grel->gr_sym,realsym); - grel->gr_type = relp->r_info[7]; - } else { - grel->gr_sym = grel->gr_info >>32; - grel->gr_type = grel->gr_info & 0xffffffff; - } - grel->gr_isrela = FALSE; - - } - return DW_DLV_OK; -} -#endif /* 0 */ - -#if 0 -int -dwarf_load_elf_dynstr( - dwarf_elf_object_access_internals_t *ep, int *errcode) -{ - struct generic_shdr *strpsh = 0; - int res = 0; - Dwarf_Unsigned strsectindex =0; - Dwarf_Unsigned strsectlength = 0; - - if (!ep->f_dynsym_sect_strings_sect_index) { - return DW_DLV_NO_ENTRY; - } - strsectindex = ep->f_dynsym_sect_strings_sect_index; - strsectlength = ep->f_dynsym_sect_strings_max; - strpsh = ep->f_shdr + strsectindex; - /* Alloc an extra byte as a guaranteed NUL byte - at the end of the strings in case the section - is corrupted and lacks a NUL at end. */ - ep->f_dynsym_sect_strings = calloc(1,strsectlength+1); - if (!ep->f_dynsym_sect_strings) { - ep->f_dynsym_sect_strings = 0; - ep->f_dynsym_sect_strings_max = 0; - ep->f_dynsym_sect_strings_sect_index = 0; - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = RRMOA(ep->f_fd,ep->f_dynsym_sect_strings, - strpsh->gh_offset, - strsectlength, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - ep->f_dynsym_sect_strings = 0; - ep->f_dynsym_sect_strings_max = 0; - ep->f_dynsym_sect_strings_sect_index = 0; - return res; - } - return DW_DLV_OK; -} -#endif /* 0 */ - -int -_dwarf_load_elf_symstr( - dwarf_elf_object_access_internals_t *ep, int *errcode) -{ - struct generic_shdr *strpsh = 0; - int res = 0; - Dwarf_Unsigned strsectindex =0; - Dwarf_Unsigned strsectlength = 0; - - if (!ep->f_symtab_sect_strings_sect_index) { - return DW_DLV_NO_ENTRY; - } - strsectindex = ep->f_symtab_sect_strings_sect_index; - strsectlength = ep->f_symtab_sect_strings_max; - strpsh = ep->f_shdr + strsectindex; - /* Alloc an extra byte as a guaranteed NUL byte - at the end of the strings in case the section - is corrupted and lacks a NUL at end. */ - ep->f_symtab_sect_strings = calloc(1,strsectlength+1); - if (!ep->f_symtab_sect_strings) { - ep->f_symtab_sect_strings = 0; - ep->f_symtab_sect_strings_max = 0; - ep->f_symtab_sect_strings_sect_index = 0; - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = RRMOA(ep->f_fd,ep->f_symtab_sect_strings, - strpsh->gh_offset, - strsectlength, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - free(ep->f_symtab_sect_strings); - ep->f_symtab_sect_strings = 0; - ep->f_symtab_sect_strings_max = 0; - ep->f_symtab_sect_strings_sect_index = 0; - return res; - } - return DW_DLV_OK; -} - - -static int -_dwarf_elf_load_sectstrings( - dwarf_elf_object_access_internals_t *ep, - Dwarf_Unsigned stringsection, - int *errcode) -{ - int res = 0; - struct generic_shdr *psh = 0; - Dwarf_Unsigned secoffset = 0; - - ep->f_elf_shstrings_length = 0; - if (stringsection >= ep->f_ehdr->ge_shnum) { - *errcode = DW_DLE_SECTION_INDEX_BAD; - return DW_DLV_ERROR; - } - psh = ep->f_shdr + stringsection; - secoffset = psh->gh_offset; - if (is_empty_section(psh->gh_type)) { - *errcode = DW_DLE_ELF_STRING_SECTION_MISSING; - return DW_DLV_ERROR; - } - if (psh->gh_size > ep->f_elf_shstrings_max) { - free(ep->f_elf_shstrings_data); - ep->f_elf_shstrings_data = (char *)malloc(psh->gh_size); - ep->f_elf_shstrings_max = psh->gh_size; - if (!ep->f_elf_shstrings_data) { - ep->f_elf_shstrings_max = 0; - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - } - ep->f_elf_shstrings_length = psh->gh_size; - res = RRMOA(ep->f_fd,ep->f_elf_shstrings_data,secoffset, - psh->gh_size, - ep->f_filesize,errcode); - return res; -} - -static int -elf_load_sectheaders32( - dwarf_elf_object_access_internals_t *ep, - Dwarf_Unsigned offset,Dwarf_Unsigned entsize, - Dwarf_Unsigned count,int *errcode) -{ - Dwarf_Unsigned generic_count = 0; - int res = 0; - - - if (count == 0) { - return DW_DLV_NO_ENTRY; - } - if (entsize < sizeof(dw_elf32_shdr)) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - if ((offset > ep->f_filesize) || - (entsize > 200) || - (count > ep->f_filesize) || - ((count *entsize +offset) > ep->f_filesize)) { - *errcode = DW_DLE_FILE_OFFSET_BAD; - return DW_DLV_ERROR; - } - res = generic_shdr_from_shdr32(ep,&generic_count, - offset,entsize,count,errcode); - if (res != DW_DLV_OK) { - return res; - } - if (generic_count != count) { - *errcode = DW_DLE_ELF_SECTION_COUNT_MISMATCH; - return DW_DLV_ERROR; - } - return DW_DLV_OK; -} - -static int -elf_load_sectheaders64( - dwarf_elf_object_access_internals_t *ep, - Dwarf_Unsigned offset,Dwarf_Unsigned entsize, - Dwarf_Unsigned count,int*errcode) -{ - Dwarf_Unsigned generic_count = 0; - int res = 0; - - - if (count == 0) { - return DW_DLV_NO_ENTRY; - } - if (entsize < sizeof(dw_elf64_shdr)) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - if ((offset > ep->f_filesize) || - (entsize > 200) || - (count > ep->f_filesize) || - ((count *entsize +offset) > ep->f_filesize)) { - *errcode = DW_DLE_FILE_OFFSET_BAD; - return DW_DLV_ERROR; - } - res = generic_shdr_from_shdr64(ep,&generic_count, - offset,entsize,count,errcode); - if (res != DW_DLV_OK) { - return res; - } - if (generic_count != count) { - *errcode = DW_DLE_ELF_SECTION_COUNT_MISMATCH; - return DW_DLV_ERROR; - } - return DW_DLV_OK; -} - - -static int -_dwarf_elf_load_rela_32( - dwarf_elf_object_access_internals_t *ep, - struct generic_shdr * gsh, - struct generic_rela ** grel_out, - Dwarf_Unsigned *count_out, int *errcode) -{ - Dwarf_Unsigned count = 0; - Dwarf_Unsigned size = 0; - Dwarf_Unsigned size2 = 0; - Dwarf_Unsigned sizeg = 0; - Dwarf_Unsigned offset = 0; - int res = 0; - dw_elf32_rela *relp = 0; - Dwarf_Unsigned object_reclen = sizeof(dw_elf32_rela); - struct generic_rela *grel = 0; - - offset = gsh->gh_offset; - size = gsh->gh_size; - if (size == 0) { - return DW_DLV_NO_ENTRY; - } - if ((offset > ep->f_filesize) || - (size > ep->f_filesize) || - ((size +offset) > ep->f_filesize)) { - *errcode = DW_DLE_FILE_OFFSET_BAD; - return DW_DLV_ERROR; - } - - count = (long)(size/object_reclen); - size2 = count * object_reclen; - if (size != size2) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - relp = (dw_elf32_rela *)malloc(size); - if (!relp) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = RRMOA(ep->f_fd,relp,offset,size, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - free(relp); - return res; - } - sizeg = count*sizeof(struct generic_rela); - grel = (struct generic_rela *)malloc(sizeg); - if (!grel) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = generic_rel_from_rela32(ep,gsh,relp,grel,errcode); - free(relp); - if (res == DW_DLV_OK) { - gsh->gh_relcount = count; - gsh->gh_rels = grel; - *count_out = count; - *grel_out = grel; - return res; - } - /* Some sort of issue */ - count_out = 0; - free(grel); - return res; -} - -#if 0 -static int -_dwarf_elf_load_rel_32( - dwarf_elf_object_access_internals_t *ep, - struct generic_shdr * gsh,struct generic_rela ** grel_out, - Dwarf_Unsigned *count_out,int *errcode) -{ - Dwarf_Unsigned count = 0; - Dwarf_Unsigned size = 0; - Dwarf_Unsigned size2 = 0; - Dwarf_Unsigned sizeg = 0; - Dwarf_Unsigned offset = 0; - int res = 0; - dw_elf32_rel* relp = 0; - Dwarf_Unsigned object_reclen = sizeof(dw_elf32_rel); - struct generic_rela *grel = 0; - - offset = gsh->gh_offset; - size = gsh->gh_size; - if (size == 0) { - return DW_DLV_NO_ENTRY; - } - if ((offset > ep->f_filesize) || - (size > ep->f_filesize) || - ((size +offset) > ep->f_filesize)) { - *errcode = DW_DLE_FILE_OFFSET_BAD; - return DW_DLV_ERROR; - } - - count = size/object_reclen; - size2 = count * object_reclen; - if (size != size2) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - relp = (dw_elf32_rel *)malloc(size); - if (!relp) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = RRMOA(ep->f_fd,relp,offset,size, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - free(relp); - return res; - } - sizeg = count *sizeof(struct generic_rela); - grel = (struct generic_rela *)malloc(sizeg); - if (!grel) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = generic_rel_from_rel32(ep,gsh,relp,grel,errcode); - free(relp); - if (res == DW_DLV_OK) { - *count_out = count; - *grel_out = grel; - return res; - } - /* Some sort of error */ - count_out = 0; - free (grel); - return res; -} -#endif /* 0 */ - -#if 0 -static int -_dwarf_elf_load_rel_64( - dwarf_elf_object_access_internals_t *ep, - struct generic_shdr * gsh,struct generic_rela ** grel_out, - Dwarf_Unsigned *count_out,int *errcode) -{ - Dwarf_Unsigned count = 0; - Dwarf_Unsigned size = 0; - Dwarf_Unsigned size2 = 0; - Dwarf_Unsigned sizeg = 0; - Dwarf_Unsigned offset = 0; - int res = 0; - dw_elf64_rel* relp = 0; - Dwarf_Unsigned object_reclen = sizeof(dw_elf64_rel); - struct generic_rela *grel = 0; - - offset = gsh->gh_offset; - size = gsh->gh_size; - if (size == 0) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - if ((offset > ep->f_filesize) || - (size > ep->f_filesize) || - ((size +offset) > ep->f_filesize)) { - *errcode = DW_DLE_FILE_OFFSET_BAD; - return DW_DLV_ERROR; - } - - count = size/object_reclen; - size2 = count * object_reclen; - if (size != size2) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - relp = (dw_elf64_rel *)malloc(size); - if (!relp) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = RRMOA(ep->f_fd,relp,offset,size, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - free(relp); - return res; - } - sizeg = count*sizeof(struct generic_rela); - grel = (struct generic_rela *)malloc(sizeg); - if (!grel) { - free(relp); - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = generic_rel_from_rel64(ep,gsh,relp,grel,errcode); - free(relp); - if (res == DW_DLV_OK) { - *count_out = count; - *grel_out = grel; - return res; - } - /* Some sort of error */ - count_out = 0; - free (grel); - return res; -} -#endif /* 0 */ - - -static int -_dwarf_elf_load_rela_64( - dwarf_elf_object_access_internals_t *ep, - struct generic_shdr * gsh, - struct generic_rela ** grel_out, - Dwarf_Unsigned *count_out,int *errcode) -{ - Dwarf_Unsigned count = 0; - Dwarf_Unsigned size = 0; - Dwarf_Unsigned size2 = 0; - Dwarf_Unsigned sizeg = 0; - Dwarf_Unsigned offset = 0; - int res = 0; - dw_elf64_rela *relp = 0; - Dwarf_Unsigned object_reclen = sizeof(dw_elf64_rela); - struct generic_rela *grel = 0; - - offset = gsh->gh_offset; - size = gsh->gh_size; - if (size == 0) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - if ((offset > ep->f_filesize) || - (size > ep->f_filesize) || - ((size +offset) > ep->f_filesize)) { - *errcode = DW_DLE_FILE_OFFSET_BAD; - return DW_DLV_ERROR; - } - count = (long)(size/object_reclen); - size2 = count * object_reclen; - if (size != size2) { - *errcode = DW_DLE_SECTION_SIZE_ERROR; - return DW_DLV_ERROR; - } - /* Here want native rela size from the file */ - relp = (dw_elf64_rela *)malloc(size); - if (!relp) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = RRMOA(ep->f_fd,relp,offset,size, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - free(relp); - return res; - } - sizeg = count*sizeof(struct generic_rela); - /* Here want generic-record size from the file */ - grel = (struct generic_rela *)malloc(sizeg); - if (!grel) { - free(relp); - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = generic_rel_from_rela64(ep,gsh,relp,grel,errcode); - free(relp); - if (res == DW_DLV_OK) { - *count_out = count; - *grel_out = grel; - return res; - } - /* Some sort of error */ - count_out = 0; - free (grel); - return res; -} - -/* Is this rela section related to dwarf at all? - set oksecnum zero if not. Else set targ secnum. - Never returns DW_DLV_NO_ENTRY. */ -static int -this_is_a_section_dwarf_related( - dwarf_elf_object_access_internals_t *ep, - struct generic_shdr *gshdr, - unsigned *oksecnum_out, - int *errcode) -{ - unsigned oksecnum = 0; - struct generic_shdr *gstarg = 0; - - if (gshdr->gh_type != SHT_RELA) { - *oksecnum_out = 0; - return DW_DLV_OK; - } - oksecnum = gshdr->gh_reloc_target_secnum; - if (oksecnum >= ep->f_loc_shdr.g_count) { - *oksecnum_out = 0; - *errcode = DW_DLE_ELF_SECTION_ERROR; - return DW_DLV_ERROR; - } - gstarg = ep->f_shdr+oksecnum; - if (!gstarg->gh_is_dwarf) { - *oksecnum_out = 0; /* no reloc needed. */ - return DW_DLV_OK; - } - - *oksecnum_out = oksecnum; - return DW_DLV_OK; -} -/* Secnum here is the secnum of rela. Not - the target of the relocations. */ -int -_dwarf_load_elf_rela( - dwarf_elf_object_access_internals_t *ep, - Dwarf_Unsigned secnum, - int *errcode) -{ - struct generic_shdr *gshdr = 0; - Dwarf_Unsigned seccount = 0; - unsigned offsetsize = 0; - struct generic_rela *grp = 0; - Dwarf_Unsigned count_read = 0; - int res = 0; - unsigned oksec = 0; - - if (!ep) { - *errcode = DW_DLE_INTERNAL_NULL_POINTER; - return DW_DLV_ERROR; - } - offsetsize = ep->f_offsetsize; - seccount = ep->f_loc_shdr.g_count; - if (secnum >= seccount) { - *errcode = DW_DLE_ELF_SECTION_ERROR; - return DW_DLV_ERROR; - } - gshdr = ep->f_shdr +secnum; - if (is_empty_section(gshdr->gh_type)) { - return DW_DLV_NO_ENTRY; - } - - res = this_is_a_section_dwarf_related(ep,gshdr,&oksec,errcode); - if (res == DW_DLV_ERROR) { - return res; - } - if (!oksec) { - return DW_DLV_OK; - } - /* We will actually read these relocations. - Others get ignored. */ - if (offsetsize == 32) { - res = _dwarf_elf_load_rela_32(ep, - gshdr,&grp,&count_read,errcode); - } else if (offsetsize == 64) { - res = _dwarf_elf_load_rela_64(ep, - gshdr,&grp,&count_read,errcode); - } else { - *errcode = DW_DLE_OFFSET_SIZE; - return DW_DLV_ERROR; - } - if (res == DW_DLV_ERROR) { - return res; - } - if (res == DW_DLV_NO_ENTRY) { - return res; - } - gshdr->gh_rels = grp; - gshdr->gh_relcount = count_read; - return DW_DLV_OK; -} -#if 0 -int -_dwarf_load_elf_rel( - dwarf_elf_object_access_internals_t *ep, - Dwarf_Unsigned secnum, int *errcode) -{ - struct generic_shdr *gshdr = 0; - Dwarf_Unsigned generic_count = 0; - unsigned offsetsize = 0; - struct generic_rela *grp = 0; - Dwarf_Unsigned count_read = 0; - int res = 0; - - if (!ep) { - *errcode = DW_DLE_INTERNAL_NULL_POINTER; - return DW_DLV_ERROR; - } - offsetsize = ep->f_offsetsize; - generic_count = ep->f_loc_shdr.g_count; - if (secnum >= generic_count) { - *errcode = DW_DLE_ELF_SECTION_ERROR; - return DW_DLV_ERROR; - } - gshdr = ep->f_shdr +secnum; - if (is_empty_section(gshdr->gh_type)) { - return DW_DLV_NO_ENTRY; - } - if (offsetsize == 32) { - res = _dwarf_elf_load_rel_32(ep, - gshdr,&grp,&count_read,errcode); - } else if (offsetsize == 64) { - res = _dwarf_elf_load_rel_64(ep, - gshdr,&grp,&count_read,errcode); - } else { - *errcode = DW_DLE_OFFSET_SIZE; - return DW_DLV_ERROR; - } - if (res == DW_DLV_ERROR) { - return res; - } - if (res == DW_DLV_NO_ENTRY) { - return res; - } - gshdr->gh_rels = grp; - gshdr->gh_relcount = count_read; - return DW_DLV_OK; -} -#endif /* 0 */ - -static int -validate_section_name_string(Dwarf_Unsigned section_length, - Dwarf_Unsigned string_loc_index, - const char * strings_start, - int * errcode) -{ - const char *endpoint = strings_start + section_length; - const char *cur = 0; - - if (section_length <= string_loc_index) { - *errcode = DW_DLE_SECTION_STRING_OFFSET_BAD; - return DW_DLV_ERROR; - } - cur = string_loc_index+strings_start; - for ( ; cur < endpoint; ++cur) { - if (!*cur) { - return DW_DLV_OK; - } - } - *errcode = DW_DLE_SECTION_STRING_OFFSET_BAD; - return DW_DLV_ERROR; -} - -static int -_dwarf_elf_load_sect_namestring( - dwarf_elf_object_access_internals_t *ep, - int *errcode) -{ - struct generic_shdr *gshdr = 0; - Dwarf_Unsigned generic_count = 0; - Dwarf_Unsigned i = 1; - const char *stringsecbase = 0; - - stringsecbase = ep->f_elf_shstrings_data; - gshdr = ep->f_shdr; - generic_count = ep->f_loc_shdr.g_count; - for (i = 0; i < generic_count; i++, ++gshdr) { - const char *namestr = - ""; - int res = 0; - - res = validate_section_name_string(ep->f_elf_shstrings_length, - gshdr->gh_name, stringsecbase, - errcode); - if (res != DW_DLV_OK) { - gshdr->gh_namestring = namestr; - return res; - } - gshdr->gh_namestring = stringsecbase + gshdr->gh_name; - } - return DW_DLV_OK; -} - - -static int -elf_load_elf_header32( - dwarf_elf_object_access_internals_t *ep,int *errcode) -{ - int res = 0; - dw_elf32_ehdr ehdr32; - struct generic_ehdr *ehdr = 0; - - res = RRMOA(ep->f_fd,&ehdr32,0,sizeof(ehdr32), - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - return res; - } - ehdr = (struct generic_ehdr *)calloc(1, - sizeof(struct generic_ehdr)); - if (!ehdr) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = generic_ehdr_from_32(ep,ehdr,&ehdr32,errcode); - return res; -} -static int -elf_load_elf_header64( - dwarf_elf_object_access_internals_t *ep,int *errcode) -{ - int res = 0; - dw_elf64_ehdr ehdr64; - struct generic_ehdr *ehdr = 0; - - res = RRMOA(ep->f_fd,&ehdr64,0,sizeof(ehdr64), - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - return res; - } - ehdr = (struct generic_ehdr *)calloc(1, - sizeof(struct generic_ehdr)); - if (!ehdr) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - res = generic_ehdr_from_64(ep,ehdr,&ehdr64,errcode); - return res; -} - -static int -validate_struct_sizes( -#ifdef HAVE_ELF_H - int*errcode -#else - UNUSEDARG int*errcode -#endif - ) -{ -#ifdef HAVE_ELF_H - /* This is a sanity check when we have an elf.h - to check against. */ - if (sizeof(Elf32_Ehdr) != sizeof(dw_elf32_ehdr)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf64_Ehdr) != sizeof(dw_elf64_ehdr)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf32_Shdr) != sizeof(dw_elf32_shdr)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf64_Shdr) != sizeof(dw_elf64_shdr)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf32_Phdr) != sizeof(dw_elf32_phdr)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf64_Phdr) != sizeof(dw_elf64_phdr)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf32_Rel) != sizeof(dw_elf32_rel)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf64_Rel) != sizeof(dw_elf64_rel)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf32_Rela) != sizeof(dw_elf32_rela)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf64_Rela) != sizeof(dw_elf64_rela)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf32_Sym) != sizeof(dw_elf32_sym)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } - if (sizeof(Elf64_Sym) != sizeof(dw_elf64_sym)) { - *errcode = DW_DLE_BAD_TYPE_SIZE; - return DW_DLV_ERROR; - } -#endif /* HAVE_ELF_H */ - return DW_DLV_OK; -} - -int -_dwarf_load_elf_header( - dwarf_elf_object_access_internals_t *ep,int*errcode) -{ - unsigned offsetsize = ep->f_offsetsize; - int res = 0; - - res = validate_struct_sizes(errcode); - if (res != DW_DLV_OK) { - return res; - } - - if (offsetsize == 32) { - res = elf_load_elf_header32(ep,errcode); - } else if (offsetsize == 64) { - if (sizeof(Dwarf_Unsigned) < 8) { - *errcode = DW_DLE_INTEGER_TOO_SMALL; - return DW_DLV_ERROR; - } - res = elf_load_elf_header64(ep,errcode); - } else { - *errcode = DW_DLE_OFFSET_SIZE; - return DW_DLV_ERROR; - } - return res; -} - -static int -validate_links( - dwarf_elf_object_access_internals_t *ep, - Dwarf_Unsigned knownsect, - Dwarf_Unsigned string_sect, - int *errcode) -{ - struct generic_shdr* pshk = 0; - - if (!knownsect) { - return DW_DLV_OK; - } - if (!string_sect) { - *errcode = DW_DLE_ELF_STRING_SECTION_ERROR; - return DW_DLV_ERROR; - } - pshk = ep->f_shdr + knownsect; - if (string_sect != pshk->gh_link) { - *errcode = DW_DLE_ELF_SECTION_LINK_ERROR; - return DW_DLV_ERROR; - } - return DW_DLV_OK; -} - - -static int -string_endswith(const char *n,const char *q) -{ - unsigned long len = strlen(n); - unsigned long qlen = strlen(q); - const char *startpt = 0; - - if (len < qlen) { - return FALSE; - } - startpt = n + (len-qlen); - if (strcmp(startpt,q)) { - return FALSE; - } - return TRUE; -} - -/* We are allowing either SHT_GROUP or .group to indicate - a group section, but really one should have both - or neither! */ -static int -elf_sht_groupsec(Dwarf_Unsigned type, const char *sname) -{ - /* ARM compilers name SHT group "__ARM_grp" - not .group */ - if ((type == SHT_GROUP) || (!strcmp(sname,".group"))) { - return TRUE; - } - return FALSE; -} - -static int -elf_flagmatches(Dwarf_Unsigned flagsword,Dwarf_Unsigned flag) -{ - if ((flagsword&flag) == flag) { - return TRUE; - } - return FALSE; -} - -/* For SHT_GROUP sections. */ -static int -read_gs_section_group( - dwarf_elf_object_access_internals_t *ep, - struct generic_shdr* psh, - int *errcode) -{ - Dwarf_Unsigned i = 0; - int res = 0; - - if (!psh->gh_sht_group_array) { - Dwarf_Unsigned seclen = psh->gh_size; - char *data = 0; - char *dp = 0; - Dwarf_Unsigned* grouparray = 0; - char dblock[4]; - Dwarf_Unsigned va = 0; - Dwarf_Unsigned count = 0; - int foundone = 0; - - if (seclen < DWARF_32BIT_SIZE) { - *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; - return DW_DLV_ERROR; - } - data = malloc(seclen); - if (!data) { - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - dp = data; - count = seclen/psh->gh_entsize; - if (count > ep->f_loc_shdr.g_count) { - /* Impossible */ - free(data); - *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; - return DW_DLV_ERROR; - } - - if (psh->gh_entsize != DWARF_32BIT_SIZE) { - *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; - free(data); - return DW_DLV_ERROR; - } - res = RRMOA(ep->f_fd,data,psh->gh_offset,seclen, - ep->f_filesize,errcode); - if (res != DW_DLV_OK) { - free(data); - return res; - } - grouparray = malloc(count * sizeof(Dwarf_Unsigned)); - if (!grouparray) { - free(data); - *errcode = DW_DLE_ALLOC_FAIL; - return DW_DLV_ERROR; - } - - memcpy(dblock,dp,DWARF_32BIT_SIZE); - ASNAR(memcpy,va,dblock); - /* There is ambiguity on the endianness of this stuff. */ - if (va != 1 && va != 0x1000000) { - /* Could be corrupted elf object. */ - *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; - free(data); - free(grouparray); - return DW_DLV_ERROR; - } - grouparray[0] = 1; - dp = dp + DWARF_32BIT_SIZE; - for (i = 1; i < count; ++i,dp += DWARF_32BIT_SIZE) { - Dwarf_Unsigned gseca = 0; - Dwarf_Unsigned gsecb = 0; - struct generic_shdr* targpsh = 0; - - memcpy(dblock,dp,DWARF_32BIT_SIZE); - ASNAR(memcpy,gseca,dblock); - ASNAR(_dwarf_memcpy_swap_bytes,gsecb,dblock); - if (!gseca) { - free(data); - free(grouparray); - *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; - return DW_DLV_ERROR; - } - grouparray[i] = gseca; - if (gseca > ep->f_loc_shdr.g_count) { - /* Might be confused endianness by - the compiler generating the SHT_GROUP. - This is pretty horrible. */ - - if (gsecb > ep->f_loc_shdr.g_count) { - *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; - free(data); - free(grouparray); - return DW_DLV_ERROR; - } - /* Ok. Yes, ugly. */ - gseca = gsecb; - grouparray[i] = gseca; - } - targpsh = ep->f_shdr + gseca; - if (targpsh->gh_section_group_number) { - /* multi-assignment to groups. Oops. */ - free(data); - free(grouparray); - *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; - return DW_DLV_ERROR; - } - targpsh->gh_section_group_number = - ep->f_sg_next_group_number; - foundone = 1; - } - if (foundone) { - ++ep->f_sg_next_group_number; - ++ep->f_sht_group_type_section_count; - } - free(data); - psh->gh_sht_group_array = grouparray; - psh->gh_sht_group_array_count = count; - } - return DW_DLV_OK; -} -/* Does related things. - A) Counts the number of SHT_GROUP - and for each builds an array of the sections in the group - (which we expect are all DWARF-related) - and sets the group number in each mentioned section. - B) Counts the number of SHF_GROUP flags. - C) If gnu groups: - ensure all the DWARF sections marked with right group - based on A(we will mark unmarked as group 1, - DW_GROUPNUMBER_BASE). - D) If arm groups (SHT_GROUP zero, SHF_GROUP non-zero): - Check the relocations of all SHF_GROUP section - FIXME: algorithm needed. - - - If SHT_GROUP and SHF_GROUP this is GNU groups. - If no SHT_GROUP and have SHF_GROUP this is - arm cc groups and we must use relocation information - to identify the group members. - - It seems(?) impossible for an object to have both - dwo sections and (SHF_GROUP or SHT_GROUP), but - we do not rule that out here. */ -static int -_dwarf_elf_setup_all_section_groups( - dwarf_elf_object_access_internals_t *ep, - int *errcode) -{ - struct generic_shdr* psh = 0; - Dwarf_Unsigned i = 0; - Dwarf_Unsigned count = 0; - int res = 0; - - count = ep->f_loc_shdr.g_count; - psh = ep->f_shdr; - - /* Does step A and step B */ - for (i = 0; i < count; ++psh,++i) { - const char *name = psh->gh_namestring; - if (is_empty_section(psh->gh_type)) { - /* No data here. */ - continue; - } - if (!elf_sht_groupsec(psh->gh_type,name)) { - /* Step B */ - if (elf_flagmatches(psh->gh_flags,SHF_GROUP)) { - ep->f_shf_group_flag_section_count++; - } - continue; - } - /* Looks like a section group. Do Step A. */ - res =read_gs_section_group(ep,psh,errcode); - if (res != DW_DLV_OK) { - return res; - } - } - /* Any sections not marked above or here are in - grep DW_GROUPNUMBER_BASE (1). - Section C. */ - psh = ep->f_shdr; - for (i = 0; i < count; ++psh,++i) { - const char *name = psh->gh_namestring; - - if (is_empty_section(psh->gh_type)) { - /* No data here. */ - continue; - } - if (elf_sht_groupsec(psh->gh_type,name)) { - continue; - } - /* Not a section group */ - if (string_endswith(name,".dwo")) { - if (psh->gh_section_group_number) { - /* multi-assignment to groups. Oops. */ - *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; - return DW_DLV_ERROR; - } - psh->gh_is_dwarf = TRUE; - psh->gh_section_group_number = DW_GROUPNUMBER_DWO; - ep->f_dwo_group_section_count++; - } else if (_dwarf_load_elf_section_is_dwarf(name)) { - if (!psh->gh_section_group_number) { - psh->gh_section_group_number = DW_GROUPNUMBER_BASE; - } - psh->gh_is_dwarf = TRUE; - } else { - /* Do nothing. */ - } - } - if (ep->f_sht_group_type_section_count) { - /* Not ARM. Done. */ - } - if (!ep->f_shf_group_flag_section_count) { - /* Nothing more to do. */ - return DW_DLV_OK; - } - return DW_DLV_OK; -} - -static int -_dwarf_elf_find_sym_sections( - dwarf_elf_object_access_internals_t *ep, - int *errcode) -{ - struct generic_shdr* psh = 0; - Dwarf_Unsigned i = 0; - Dwarf_Unsigned count = 0; - int res = 0; - - count = ep->f_loc_shdr.g_count; - psh = ep->f_shdr; - for (i = 0; i < count; ++psh,++i) { - const char *name = psh->gh_namestring; - if (is_empty_section(psh->gh_type)) { - /* No data here. */ - continue; - } - if (!strcmp(name,".dynsym")) { - ep->f_dynsym_sect_index = i; - ep->f_loc_dynsym.g_offset = psh->gh_offset; - } else if (!strcmp(name,".dynstr")) { - ep->f_dynsym_sect_strings_sect_index = i; - ep->f_dynsym_sect_strings_max = psh->gh_size; - } else if (!strcmp(name,".symtab")) { - ep->f_symtab_sect_index = i; - ep->f_loc_symtab.g_offset = psh->gh_offset; - } else if (!strcmp(name,".strtab")) { - ep->f_symtab_sect_strings_sect_index = i; - ep->f_symtab_sect_strings_max = psh->gh_size; - } else if (!strcmp(name,".dynamic")) { - ep->f_dynamic_sect_index = i; - ep->f_loc_dynamic.g_offset = psh->gh_offset; - } - } - -#if 0 - res = validate_links(ep,ep->f_dynsym_sect_index, - ep->f_dynsym_sect_strings_sect_index,errcode); - if (res!= DW_DLV_OK) { - return res; - } -#endif /* 0 */ - res = validate_links(ep,ep->f_symtab_sect_index, - ep->f_symtab_sect_strings_sect_index,errcode); - if (res!= DW_DLV_OK) { - return res; - } - return DW_DLV_OK; -} - - -int -_dwarf_load_elf_sectheaders( - dwarf_elf_object_access_internals_t *ep,int*errcode) -{ - int res = 0; - - if (ep->f_offsetsize == 32) { - res = elf_load_sectheaders32(ep,ep->f_ehdr->ge_shoff, - ep->f_ehdr->ge_shentsize, - ep->f_ehdr->ge_shnum,errcode); - } else if (ep->f_offsetsize == 64) { - res = elf_load_sectheaders64(ep,ep->f_ehdr->ge_shoff, - ep->f_ehdr->ge_shentsize, - ep->f_ehdr->ge_shnum,errcode); - } else { - *errcode = DW_DLE_OFFSET_SIZE; - return DW_DLV_ERROR; - } - if (res != DW_DLV_OK) { - return res; - } - res = _dwarf_elf_load_sectstrings(ep, - ep->f_ehdr->ge_shstrndx,errcode); - if (res != DW_DLV_OK) { - return res; - } - res = _dwarf_elf_load_sect_namestring(ep,errcode); - if (res != DW_DLV_OK) { - return res; - } - res = _dwarf_elf_find_sym_sections(ep,errcode); - if (res != DW_DLV_OK) { - return res; - } - res = _dwarf_elf_setup_all_section_groups(ep,errcode); - return res; -} diff --git a/test/bug-hunting/cve/CVE-2019-14249/expected.txt b/test/bug-hunting/cve/CVE-2019-14249/expected.txt deleted file mode 100644 index 12aae048605..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14249/expected.txt +++ /dev/null @@ -1 +0,0 @@ -dwarf_elf_load_headers.c:1838:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-14284/README b/test/bug-hunting/cve/CVE-2019-14284/README deleted file mode 100644 index 0bfc1e4e831..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14284/README +++ /dev/null @@ -1,5 +0,0 @@ - -Details: -https://nvd.nist.gov/vuln/detail/CVE-2019-14284 - - diff --git a/test/bug-hunting/cve/CVE-2019-14284/expected.txt b/test/bug-hunting/cve/CVE-2019-14284/expected.txt deleted file mode 100644 index efc4a1bd48d..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14284/expected.txt +++ /dev/null @@ -1 +0,0 @@ -floppy.c:2131:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-14284/floppy.c b/test/bug-hunting/cve/CVE-2019-14284/floppy.c deleted file mode 100644 index b3241d81f41..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14284/floppy.c +++ /dev/null @@ -1,4981 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * linux/drivers/block/floppy.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 1993, 1994 Alain Knaff - * Copyright (C) 1998 Alan Cox - */ - -/* - * 02.12.91 - Changed to static variables to indicate need for reset - * and recalibrate. This makes some things easier (output_byte reset - * checking etc), and means less interrupt jumping in case of errors, - * so the code is hopefully easier to understand. - */ - -/* - * This file is certainly a mess. I've tried my best to get it working, - * but I don't like programming floppies, and I have only one anyway. - * Urgel. I should check for more errors, and do more graceful error - * recovery. Seems there are problems with several drives. I've tried to - * correct them. No promises. - */ - -/* - * As with hd.c, all routines within this file can (and will) be called - * by interrupts, so extreme caution is needed. A hardware interrupt - * handler may not sleep, or a kernel panic will happen. Thus I cannot - * call "floppy-on" directly, but have to set a special timer interrupt - * etc. - */ - -/* - * 28.02.92 - made track-buffering routines, based on the routines written - * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus. - */ - -/* - * Automatic floppy-detection and formatting written by Werner Almesberger - * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with - * the floppy-change signal detection. - */ - -/* - * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed - * FDC data overrun bug, added some preliminary stuff for vertical - * recording support. - * - * 1992/9/17: Added DMA allocation & DMA functions. -- hhb. - * - * TODO: Errors are still not counted properly. - */ - -/* 1992/9/20 - * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl) - * modeled after the freeware MS-DOS program fdformat/88 V1.8 by - * Christoph H. Hochst\"atter. - * I have fixed the shift values to the ones I always use. Maybe a new - * ioctl() should be created to be able to modify them. - * There is a bug in the driver that makes it impossible to format a - * floppy as the first thing after bootup. - */ - -/* - * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and - * this helped the floppy driver as well. Much cleaner, and still seems to - * work. - */ - -/* 1994/6/24 --bbroad-- added the floppy table entries and made - * minor modifications to allow 2.88 floppies to be run. - */ - -/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more - * disk types. - */ - -/* - * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger - * format bug fixes, but unfortunately some new bugs too... - */ - -/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write - * errors to allow safe writing by specialized programs. - */ - -/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks - * by defining bit 1 of the "stretch" parameter to mean put sectors on the - * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's - * drives are "upside-down"). - */ - -/* - * 1995/8/26 -- Andreas Busse -- added Mips support. - */ - -/* - * 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependent - * features to asm/floppy.h. - */ - -/* - * 1998/1/21 -- Richard Gooch -- devfs support - */ - -/* - * 1998/05/07 -- Russell King -- More portability cleanups; moved definition of - * interrupt and dma channel to asm/floppy.h. Cleaned up some formatting & - * use of '0' for NULL. - */ - -/* - * 1998/06/07 -- Alan Cox -- Merged the 2.0.34 fixes for resource allocation - * failures. - */ - -/* - * 1998/09/20 -- David Weinehall -- Added slow-down code for buggy PS/2-drives. - */ - -/* - * 1999/08/13 -- Paul Slootman -- floppy stopped working on Alpha after 24 - * days, 6 hours, 32 minutes and 32 seconds (i.e. MAXINT jiffies; ints were - * being used to store jiffies, which are unsigned longs). - */ - -/* - * 2000/08/28 -- Arnaldo Carvalho de Melo - * - get rid of check_region - * - s/suser/capable/ - */ - -/* - * 2001/08/26 -- Paul Gortmaker - fix insmod oops on machines with no - * floppy controller (lingering task on list after module is gone... boom.) - */ - -/* - * 2002/02/07 -- Anton Altaparmakov - Fix io ports reservation to correct range - * (0x3f2-0x3f5, 0x3f7). This fix is a bit of a hack but the proper fix - * requires many non-obvious changes in arch dependent code. - */ - -/* 2003/07/28 -- Daniele Bellucci . - * Better audit of register_blkdev. - */ - -#undef FLOPPY_SILENT_DCL_CLEAR - -#define REALLY_SLOW_IO - -#define DEBUGT 2 - -#define DPRINT(format, args ...) \ - pr_info("floppy%d: " format, current_drive, ## args) - -#define DCL_DEBUG /* debug disk change line */ -#ifdef DCL_DEBUG -#define debug_dcl(test, fmt, args ...) \ - do { if ((test) & FD_DEBUG) DPRINT(fmt, ## args); } while (0) -#else -#define debug_dcl(test, fmt, args ...) \ - do { if (0) DPRINT(fmt, ## args); } while (0) -#endif - -/* do print messages for unexpected interrupts */ -static int print_unex = 1; -#include -#include -#include -#include -#include -#include -#define FDPATCHES -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* CMOS defines */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * PS/2 floppies have much slower step rates than regular floppies. - * It's been recommended that take about 1/4 of the default speed - * in some more extreme cases. - */ -static DEFINE_MUTEX(floppy_mutex); -static int slow_floppy; - -#include -#include - -static int FLOPPY_IRQ = 6; -static int FLOPPY_DMA = 2; -static int can_use_virtual_dma = 2; -/* ======= - * can use virtual DMA: - * 0 = use of virtual DMA disallowed by config - * 1 = use of virtual DMA prescribed by config - * 2 = no virtual DMA preference configured. By default try hard DMA, - * but fall back on virtual DMA when not enough memory available - */ - -static int use_virtual_dma; -/* ======= - * use virtual DMA - * 0 using hard DMA - * 1 using virtual DMA - * This variable is set to virtual when a DMA mem problem arises, and - * reset back in floppy_grab_irq_and_dma. - * It is not safe to reset it in other circumstances, because the floppy - * driver may have several buffers in use at once, and we do currently not - * record each buffers capabilities - */ - -static DEFINE_SPINLOCK(floppy_lock); - -static unsigned short virtual_dma_port = 0x3f0; -irqreturn_t floppy_interrupt(int irq, void *dev_id); -static int set_dor(int fdc, char mask, char data); - -#define K_64 0x10000 /* 64KB */ - -/* the following is the mask of allowed drives. By default units 2 and - * 3 of both floppy controllers are disabled, because switching on the - * motor of these drives causes system hangs on some PCI computers. drive - * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if - * a drive is allowed. - * - * NOTE: This must come before we include the arch floppy header because - * some ports reference this variable from there. -DaveM - */ - -static int allowed_drive_mask = 0x33; - -#include - -static int irqdma_allocated; - -#include -#include -#include /* for the compatibility eject ioctl */ -#include - -static LIST_HEAD(floppy_reqs); -static struct request *current_req; -static int set_next_request(void); - -#ifndef fd_get_dma_residue -#define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA) -#endif - -/* Dma Memory related stuff */ - -#ifndef fd_dma_mem_free -#define fd_dma_mem_free(addr, size) free_pages(addr, get_order(size)) -#endif - -#ifndef fd_dma_mem_alloc -#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL, get_order(size)) -#endif - -#ifndef fd_cacheflush -#define fd_cacheflush(addr, size) /* nothing... */ -#endif - -static inline void fallback_on_nodma_alloc(char **addr, size_t l) -{ -#ifdef FLOPPY_CAN_FALLBACK_ON_NODMA - if (*addr) - return; /* we have the memory */ - if (can_use_virtual_dma != 2) - return; /* no fallback allowed */ - pr_info("DMA memory shortage. Temporarily falling back on virtual DMA\n"); - *addr = (char *)nodma_mem_alloc(l); -#else - return; -#endif -} - -/* End dma memory related stuff */ - -static unsigned long fake_change; -static bool initialized; - -#define ITYPE(x) (((x) >> 2) & 0x1f) -#define TOMINOR(x) ((x & 3) | ((x & 4) << 5)) -#define UNIT(x) ((x) & 0x03) /* drive on fdc */ -#define FDC(x) (((x) & 0x04) >> 2) /* fdc of drive */ -/* reverse mapping from unit and fdc to drive */ -#define REVDRIVE(fdc, unit) ((unit) + ((fdc) << 2)) - -#define DP (&drive_params[current_drive]) -#define DRS (&drive_state[current_drive]) -#define DRWE (&write_errors[current_drive]) -#define FDCS (&fdc_state[fdc]) - -#define UDP (&drive_params[drive]) -#define UDRS (&drive_state[drive]) -#define UDRWE (&write_errors[drive]) -#define UFDCS (&fdc_state[FDC(drive)]) - -#define PH_HEAD(floppy, head) (((((floppy)->stretch & 2) >> 1) ^ head) << 2) -#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH) - -/* read/write */ -#define COMMAND (raw_cmd->cmd[0]) -#define DR_SELECT (raw_cmd->cmd[1]) -#define TRACK (raw_cmd->cmd[2]) -#define HEAD (raw_cmd->cmd[3]) -#define SECTOR (raw_cmd->cmd[4]) -#define SIZECODE (raw_cmd->cmd[5]) -#define SECT_PER_TRACK (raw_cmd->cmd[6]) -#define GAP (raw_cmd->cmd[7]) -#define SIZECODE2 (raw_cmd->cmd[8]) -#define NR_RW 9 - -/* format */ -#define F_SIZECODE (raw_cmd->cmd[2]) -#define F_SECT_PER_TRACK (raw_cmd->cmd[3]) -#define F_GAP (raw_cmd->cmd[4]) -#define F_FILL (raw_cmd->cmd[5]) -#define NR_F 6 - -/* - * Maximum disk size (in kilobytes). - * This default is used whenever the current disk size is unknown. - * [Now it is rather a minimum] - */ -#define MAX_DISK_SIZE 4 /* 3984 */ - -/* - * globals used by 'result()' - */ -#define MAX_REPLIES 16 -static unsigned char reply_buffer[MAX_REPLIES]; -static int inr; /* size of reply buffer, when called from interrupt */ -#define ST0 (reply_buffer[0]) -#define ST1 (reply_buffer[1]) -#define ST2 (reply_buffer[2]) -#define ST3 (reply_buffer[0]) /* result of GETSTATUS */ -#define R_TRACK (reply_buffer[3]) -#define R_HEAD (reply_buffer[4]) -#define R_SECTOR (reply_buffer[5]) -#define R_SIZECODE (reply_buffer[6]) - -#define SEL_DLY (2 * HZ / 100) - -/* - * this struct defines the different floppy drive types. - */ -static struct { - struct floppy_drive_params params; - const char *name; /* name printed while booting */ -} default_drive_params[] = { -/* NOTE: the time values in jiffies should be in msec! - CMOS drive type - | Maximum data rate supported by drive type - | | Head load time, msec - | | | Head unload time, msec (not used) - | | | | Step rate interval, usec - | | | | | Time needed for spinup time (jiffies) - | | | | | | Timeout for spinning down (jiffies) - | | | | | | | Spindown offset (where disk stops) - | | | | | | | | Select delay - | | | | | | | | | RPS - | | | | | | | | | | Max number of tracks - | | | | | | | | | | | Interrupt timeout - | | | | | | | | | | | | Max nonintlv. sectors - | | | | | | | | | | | | | -Max Errors- flags */ - {{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0, - 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" }, - - {{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0, - 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/ - - {{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0, - 0, { 2, 5, 6,23,10,20,12, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/ - - {{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0, - 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/ - - {{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0, - 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/ - - {{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0, - 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/ - - {{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0, - 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/ -/* | --autodetected formats--- | | | - * read_track | | Name printed when booting - * | Native format - * Frequency of disk change checks */ -}; - -static struct floppy_drive_params drive_params[N_DRIVE]; -static struct floppy_drive_struct drive_state[N_DRIVE]; -static struct floppy_write_errors write_errors[N_DRIVE]; -static struct timer_list motor_off_timer[N_DRIVE]; -static struct gendisk *disks[N_DRIVE]; -static struct blk_mq_tag_set tag_sets[N_DRIVE]; -static struct block_device *opened_bdev[N_DRIVE]; -static DEFINE_MUTEX(open_lock); -static struct floppy_raw_cmd *raw_cmd, default_raw_cmd; - -/* - * This struct defines the different floppy types. - * - * Bit 0 of 'stretch' tells if the tracks need to be doubled for some - * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch' - * tells if the disk is in Commodore 1581 format, which means side 0 sectors - * are located on side 1 of the disk but with a side 0 ID, and vice-versa. - * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the - * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical - * side 0 is on physical side 0 (but with the misnamed sector IDs). - * 'stretch' should probably be renamed to something more general, like - * 'options'. - * - * Bits 2 through 9 of 'stretch' tell the number of the first sector. - * The LSB (bit 2) is flipped. For most disks, the first sector - * is 1 (represented by 0x00<<2). For some CP/M and music sampler - * disks (such as Ensoniq EPS 16plus) it is 0 (represented as 0x01<<2). - * For Amstrad CPC disks it is 0xC1 (represented as 0xC0<<2). - * - * Other parameters should be self-explanatory (see also setfdprm(8)). - */ -/* - Size - | Sectors per track - | | Head - | | | Tracks - | | | | Stretch - | | | | | Gap 1 size - | | | | | | Data rate, | 0x40 for perp - | | | | | | | Spec1 (stepping rate, head unload - | | | | | | | | /fmt gap (gap2) */ -static struct floppy_struct floppy_type[32] = { - { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */ - { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */ - { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */ - { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */ - { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */ - { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */ - { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */ - { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */ - { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */ - { 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120" }, /* 9 3.12MB 3.5" */ - - { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */ - { 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */ - { 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */ - { 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */ - { 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */ - { 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */ - { 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */ - { 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */ - { 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */ - { 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */ - - { 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880" }, /* 20 880KB 5.25" */ - { 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */ - { 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */ - { 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */ - { 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */ - { 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */ - { 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */ - { 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */ - { 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */ - { 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */ - - { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */ - { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */ -}; - -#define SECTSIZE (_FD_SECTSIZE(*floppy)) - -/* Auto-detection: Disk type used until the next media change occurs. */ -static struct floppy_struct *current_type[N_DRIVE]; - -/* - * User-provided type information. current_type points to - * the respective entry of this array. - */ -static struct floppy_struct user_params[N_DRIVE]; - -static sector_t floppy_sizes[256]; - -static char floppy_device_name[] = "floppy"; - -/* - * The driver is trying to determine the correct media format - * while probing is set. rw_interrupt() clears it after a - * successful access. - */ -static int probing; - -/* Synchronization of FDC access. */ -#define FD_COMMAND_NONE -1 -#define FD_COMMAND_ERROR 2 -#define FD_COMMAND_OKAY 3 - -static volatile int command_status = FD_COMMAND_NONE; -static unsigned long fdc_busy; -static DECLARE_WAIT_QUEUE_HEAD(fdc_wait); -static DECLARE_WAIT_QUEUE_HEAD(command_done); - -/* Errors during formatting are counted here. */ -static int format_errors; - -/* Format request descriptor. */ -static struct format_descr format_req; - -/* - * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps - * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc), - * H is head unload time (1=16ms, 2=32ms, etc) - */ - -/* - * Track buffer - * Because these are written to by the DMA controller, they must - * not contain a 64k byte boundary crossing, or data will be - * corrupted/lost. - */ -static char *floppy_track_buffer; -static int max_buffer_sectors; - -static int *errors; -typedef void (*done_f)(int); -static const struct cont_t { - void (*interrupt)(void); - /* this is called after the interrupt of the - * main command */ - void (*redo)(void); /* this is called to retry the operation */ - void (*error)(void); /* this is called to tally an error */ - done_f done; /* this is called to say if the operation has - * succeeded/failed */ -} *cont; - -static void floppy_ready(void); -static void floppy_start(void); -static void process_fd_request(void); -static void recalibrate_floppy(void); -static void floppy_shutdown(struct work_struct *); - -static int floppy_request_regions(int); -static void floppy_release_regions(int); -static int floppy_grab_irq_and_dma(void); -static void floppy_release_irq_and_dma(void); - -/* - * The "reset" variable should be tested whenever an interrupt is scheduled, - * after the commands have been sent. This is to ensure that the driver doesn't - * get wedged when the interrupt doesn't come because of a failed command. - * reset doesn't need to be tested before sending commands, because - * output_byte is automatically disabled when reset is set. - */ -static void reset_fdc(void); - -/* - * These are global variables, as that's the easiest way to give - * information to interrupts. They are the data used for the current - * request. - */ -#define NO_TRACK -1 -#define NEED_1_RECAL -2 -#define NEED_2_RECAL -3 - -static atomic_t usage_count = ATOMIC_INIT(0); - -/* buffer related variables */ -static int buffer_track = -1; -static int buffer_drive = -1; -static int buffer_min = -1; -static int buffer_max = -1; - -/* fdc related variables, should end up in a struct */ -static struct floppy_fdc_state fdc_state[N_FDC]; -static int fdc; /* current fdc */ - -static struct workqueue_struct *floppy_wq; - -static struct floppy_struct *_floppy = floppy_type; -static unsigned char current_drive; -static long current_count_sectors; -static unsigned char fsector_t; /* sector in track */ -static unsigned char in_sector_offset; /* offset within physical sector, - * expressed in units of 512 bytes */ - -static inline bool drive_no_geom(int drive) -{ - return !current_type[drive] && !ITYPE(UDRS->fd_device); -} - -#ifndef fd_eject -static inline int fd_eject(int drive) -{ - return -EINVAL; -} -#endif - -/* - * Debugging - * ========= - */ -#ifdef DEBUGT -static long unsigned debugtimer; - -static inline void set_debugt(void) -{ - debugtimer = jiffies; -} - -static inline void debugt(const char *func, const char *msg) -{ - if (DP->flags & DEBUGT) - pr_info("%s:%s dtime=%lu\n", func, msg, jiffies - debugtimer); -} -#else -static inline void set_debugt(void) {} -static inline void debugt(const char *func, const char *msg) {} -#endif /* DEBUGT */ - - -static DECLARE_DELAYED_WORK(fd_timeout, floppy_shutdown); -static const char *timeout_message; - -static void is_alive(const char *func, const char *message) -{ - /* this routine checks whether the floppy driver is "alive" */ - if (test_bit(0, &fdc_busy) && command_status < 2 && - !delayed_work_pending(&fd_timeout)) { - DPRINT("%s: timeout handler died. %s\n", func, message); - } -} - -static void (*do_floppy)(void) = NULL; - -#define OLOGSIZE 20 - -static void (*lasthandler)(void); -static unsigned long interruptjiffies; -static unsigned long resultjiffies; -static int resultsize; -static unsigned long lastredo; - -static struct output_log { - unsigned char data; - unsigned char status; - unsigned long jiffies; -} output_log[OLOGSIZE]; - -static int output_log_pos; - -#define current_reqD -1 -#define MAXTIMEOUT -2 - -static void __reschedule_timeout(int drive, const char *message) -{ - unsigned long delay; - - if (drive == current_reqD) - drive = current_drive; - - if (drive < 0 || drive >= N_DRIVE) { - delay = 20UL * HZ; - drive = 0; - } else - delay = UDP->timeout; - - mod_delayed_work(floppy_wq, &fd_timeout, delay); - if (UDP->flags & FD_DEBUG) - DPRINT("reschedule timeout %s\n", message); - timeout_message = message; -} - -static void reschedule_timeout(int drive, const char *message) -{ - unsigned long flags; - - spin_lock_irqsave(&floppy_lock, flags); - __reschedule_timeout(drive, message); - spin_unlock_irqrestore(&floppy_lock, flags); -} - -#define INFBOUND(a, b) (a) = max_t(int, a, b) -#define SUPBOUND(a, b) (a) = min_t(int, a, b) - -/* - * Bottom half floppy driver. - * ========================== - * - * This part of the file contains the code talking directly to the hardware, - * and also the main service loop (seek-configure-spinup-command) - */ - -/* - * disk change. - * This routine is responsible for maintaining the FD_DISK_CHANGE flag, - * and the last_checked date. - * - * last_checked is the date of the last check which showed 'no disk change' - * FD_DISK_CHANGE is set under two conditions: - * 1. The floppy has been changed after some i/o to that floppy already - * took place. - * 2. No floppy disk is in the drive. This is done in order to ensure that - * requests are quickly flushed in case there is no disk in the drive. It - * follows that FD_DISK_CHANGE can only be cleared if there is a disk in - * the drive. - * - * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet. - * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on - * each seek. If a disk is present, the disk change line should also be - * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk - * change line is set, this means either that no disk is in the drive, or - * that it has been removed since the last seek. - * - * This means that we really have a third possibility too: - * The floppy has been changed after the last seek. - */ - -static int disk_change(int drive) -{ - int fdc = FDC(drive); - - if (time_before(jiffies, UDRS->select_date + UDP->select_delay)) - DPRINT("WARNING disk change called early\n"); - if (!(FDCS->dor & (0x10 << UNIT(drive))) || - (FDCS->dor & 3) != UNIT(drive) || fdc != FDC(drive)) { - DPRINT("probing disk change on unselected drive\n"); - DPRINT("drive=%d fdc=%d dor=%x\n", drive, FDC(drive), - (unsigned int)FDCS->dor); - } - - debug_dcl(UDP->flags, - "checking disk change line for drive %d\n", drive); - debug_dcl(UDP->flags, "jiffies=%lu\n", jiffies); - debug_dcl(UDP->flags, "disk change line=%x\n", fd_inb(FD_DIR) & 0x80); - debug_dcl(UDP->flags, "flags=%lx\n", UDRS->flags); - - if (UDP->flags & FD_BROKEN_DCL) - return test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); - if ((fd_inb(FD_DIR) ^ UDP->flags) & 0x80) { - set_bit(FD_VERIFY_BIT, &UDRS->flags); - /* verify write protection */ - - if (UDRS->maxblock) /* mark it changed */ - set_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); - - /* invalidate its geometry */ - if (UDRS->keep_data >= 0) { - if ((UDP->flags & FTD_MSG) && - current_type[drive] != NULL) - DPRINT("Disk type is undefined after disk change\n"); - current_type[drive] = NULL; - floppy_sizes[TOMINOR(drive)] = MAX_DISK_SIZE << 1; - } - - return 1; - } else { - UDRS->last_checked = jiffies; - clear_bit(FD_DISK_NEWCHANGE_BIT, &UDRS->flags); - } - return 0; -} - -static inline int is_selected(int dor, int unit) -{ - return ((dor & (0x10 << unit)) && (dor & 3) == unit); -} - -static bool is_ready_state(int status) -{ - int state = status & (STATUS_READY | STATUS_DIR | STATUS_DMA); - return state == STATUS_READY; -} - -static int set_dor(int fdc, char mask, char data) -{ - unsigned char unit; - unsigned char drive; - unsigned char newdor; - unsigned char olddor; - - if (FDCS->address == -1) - return -1; - - olddor = FDCS->dor; - newdor = (olddor & mask) | data; - if (newdor != olddor) { - unit = olddor & 0x3; - if (is_selected(olddor, unit) && !is_selected(newdor, unit)) { - drive = REVDRIVE(fdc, unit); - debug_dcl(UDP->flags, - "calling disk change from set_dor\n"); - disk_change(drive); - } - FDCS->dor = newdor; - fd_outb(newdor, FD_DOR); - - unit = newdor & 0x3; - if (!is_selected(olddor, unit) && is_selected(newdor, unit)) { - drive = REVDRIVE(fdc, unit); - UDRS->select_date = jiffies; - } - } - return olddor; -} - -static void twaddle(void) -{ - if (DP->select_delay) - return; - fd_outb(FDCS->dor & ~(0x10 << UNIT(current_drive)), FD_DOR); - fd_outb(FDCS->dor, FD_DOR); - DRS->select_date = jiffies; -} - -/* - * Reset all driver information about the current fdc. - * This is needed after a reset, and after a raw command. - */ -static void reset_fdc_info(int mode) -{ - int drive; - - FDCS->spec1 = FDCS->spec2 = -1; - FDCS->need_configure = 1; - FDCS->perp_mode = 1; - FDCS->rawcmd = 0; - for (drive = 0; drive < N_DRIVE; drive++) - if (FDC(drive) == fdc && (mode || UDRS->track != NEED_1_RECAL)) - UDRS->track = NEED_2_RECAL; -} - -/* selects the fdc and drive, and enables the fdc's input/dma. */ -static void set_fdc(int drive) -{ - if (drive >= 0 && drive < N_DRIVE) { - fdc = FDC(drive); - current_drive = drive; - } - if (fdc != 1 && fdc != 0) { - pr_info("bad fdc value\n"); - return; - } - set_dor(fdc, ~0, 8); -#if N_FDC > 1 - set_dor(1 - fdc, ~8, 0); -#endif - if (FDCS->rawcmd == 2) - reset_fdc_info(1); - if (fd_inb(FD_STATUS) != STATUS_READY) - FDCS->reset = 1; -} - -/* locks the driver */ -static int lock_fdc(int drive) -{ - if (WARN(atomic_read(&usage_count) == 0, - "Trying to lock fdc while usage count=0\n")) - return -1; - - if (wait_event_interruptible(fdc_wait, !test_and_set_bit(0, &fdc_busy))) - return -EINTR; - - command_status = FD_COMMAND_NONE; - - reschedule_timeout(drive, "lock fdc"); - set_fdc(drive); - return 0; -} - -/* unlocks the driver */ -static void unlock_fdc(void) -{ - if (!test_bit(0, &fdc_busy)) - DPRINT("FDC access conflict!\n"); - - raw_cmd = NULL; - command_status = FD_COMMAND_NONE; - cancel_delayed_work(&fd_timeout); - do_floppy = NULL; - cont = NULL; - clear_bit(0, &fdc_busy); - wake_up(&fdc_wait); -} - -/* switches the motor off after a given timeout */ -static void motor_off_callback(struct timer_list *t) -{ - unsigned long nr = t - motor_off_timer; - unsigned char mask = ~(0x10 << UNIT(nr)); - - if (WARN_ON_ONCE(nr >= N_DRIVE)) - return; - - set_dor(FDC(nr), mask, 0); -} - -/* schedules motor off */ -static void floppy_off(unsigned int drive) -{ - unsigned long volatile delta; - int fdc = FDC(drive); - - if (!(FDCS->dor & (0x10 << UNIT(drive)))) - return; - - del_timer(motor_off_timer + drive); - - /* make spindle stop in a position which minimizes spinup time - * next time */ - if (UDP->rps) { - delta = jiffies - UDRS->first_read_date + HZ - - UDP->spindown_offset; - delta = ((delta * UDP->rps) % HZ) / UDP->rps; - motor_off_timer[drive].expires = - jiffies + UDP->spindown - delta; - } - add_timer(motor_off_timer + drive); -} - -/* - * cycle through all N_DRIVE floppy drives, for disk change testing. - * stopping at current drive. This is done before any long operation, to - * be sure to have up to date disk change information. - */ -static void scandrives(void) -{ - int i; - int drive; - int saved_drive; - - if (DP->select_delay) - return; - - saved_drive = current_drive; - for (i = 0; i < N_DRIVE; i++) { - drive = (saved_drive + i + 1) % N_DRIVE; - if (UDRS->fd_ref == 0 || UDP->select_delay != 0) - continue; /* skip closed drives */ - set_fdc(drive); - if (!(set_dor(fdc, ~3, UNIT(drive) | (0x10 << UNIT(drive))) & - (0x10 << UNIT(drive)))) - /* switch the motor off again, if it was off to - * begin with */ - set_dor(fdc, ~(0x10 << UNIT(drive)), 0); - } - set_fdc(saved_drive); -} - -static void empty(void) -{} - -static void (*floppy_work_fn)(void); - -static void floppy_work_workfn(struct work_struct *work) -{ - floppy_work_fn(); -} - -static DECLARE_WORK(floppy_work, floppy_work_workfn); - -static void schedule_bh(void (*handler)(void)) -{ - WARN_ON(work_pending(&floppy_work)); - - floppy_work_fn = handler; - queue_work(floppy_wq, &floppy_work); -} - -static void (*fd_timer_fn)(void) = NULL; - -static void fd_timer_workfn(struct work_struct *work) -{ - fd_timer_fn(); -} - -static DECLARE_DELAYED_WORK(fd_timer, fd_timer_workfn); - -static void cancel_activity(void) -{ - do_floppy = NULL; - cancel_delayed_work_sync(&fd_timer); - cancel_work_sync(&floppy_work); -} - -/* this function makes sure that the disk stays in the drive during the - * transfer */ -static void fd_watchdog(void) -{ - debug_dcl(DP->flags, "calling disk change from watchdog\n"); - - if (disk_change(current_drive)) { - DPRINT("disk removed during i/o\n"); - cancel_activity(); - cont->done(0); - reset_fdc(); - } else { - cancel_delayed_work(&fd_timer); - fd_timer_fn = fd_watchdog; - queue_delayed_work(floppy_wq, &fd_timer, HZ / 10); - } -} - -static void main_command_interrupt(void) -{ - cancel_delayed_work(&fd_timer); - cont->interrupt(); -} - -/* waits for a delay (spinup or select) to pass */ -static int fd_wait_for_completion(unsigned long expires, - void (*function)(void)) -{ - if (FDCS->reset) { - reset_fdc(); /* do the reset during sleep to win time - * if we don't need to sleep, it's a good - * occasion anyways */ - return 1; - } - - if (time_before(jiffies, expires)) { - cancel_delayed_work(&fd_timer); - fd_timer_fn = function; - queue_delayed_work(floppy_wq, &fd_timer, expires - jiffies); - return 1; - } - return 0; -} - -static void setup_DMA(void) -{ - unsigned long f; - - if (raw_cmd->length == 0) { - int i; - - pr_info("zero dma transfer size:"); - for (i = 0; i < raw_cmd->cmd_count; i++) - pr_cont("%x,", raw_cmd->cmd[i]); - pr_cont("\n"); - cont->done(0); - FDCS->reset = 1; - return; - } - if (((unsigned long)raw_cmd->kernel_data) % 512) { - pr_info("non aligned address: %p\n", raw_cmd->kernel_data); - cont->done(0); - FDCS->reset = 1; - return; - } - f = claim_dma_lock(); - fd_disable_dma(); -#ifdef fd_dma_setup - if (fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length, - (raw_cmd->flags & FD_RAW_READ) ? - DMA_MODE_READ : DMA_MODE_WRITE, FDCS->address) < 0) { - release_dma_lock(f); - cont->done(0); - FDCS->reset = 1; - return; - } - release_dma_lock(f); -#else - fd_clear_dma_ff(); - fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length); - fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ) ? - DMA_MODE_READ : DMA_MODE_WRITE); - fd_set_dma_addr(raw_cmd->kernel_data); - fd_set_dma_count(raw_cmd->length); - virtual_dma_port = FDCS->address; - fd_enable_dma(); - release_dma_lock(f); -#endif -} - -static void show_floppy(void); - -/* waits until the fdc becomes ready */ -static int wait_til_ready(void) -{ - int status; - int counter; - - if (FDCS->reset) - return -1; - for (counter = 0; counter < 10000; counter++) { - status = fd_inb(FD_STATUS); - if (status & STATUS_READY) - return status; - } - if (initialized) { - DPRINT("Getstatus times out (%x) on fdc %d\n", status, fdc); - show_floppy(); - } - FDCS->reset = 1; - return -1; -} - -/* sends a command byte to the fdc */ -static int output_byte(char byte) -{ - int status = wait_til_ready(); - - if (status < 0) - return -1; - - if (is_ready_state(status)) { - fd_outb(byte, FD_DATA); - output_log[output_log_pos].data = byte; - output_log[output_log_pos].status = status; - output_log[output_log_pos].jiffies = jiffies; - output_log_pos = (output_log_pos + 1) % OLOGSIZE; - return 0; - } - FDCS->reset = 1; - if (initialized) { - DPRINT("Unable to send byte %x to FDC. Fdc=%x Status=%x\n", - byte, fdc, status); - show_floppy(); - } - return -1; -} - -/* gets the response from the fdc */ -static int result(void) -{ - int i; - int status = 0; - - for (i = 0; i < MAX_REPLIES; i++) { - status = wait_til_ready(); - if (status < 0) - break; - status &= STATUS_DIR | STATUS_READY | STATUS_BUSY | STATUS_DMA; - if ((status & ~STATUS_BUSY) == STATUS_READY) { - resultjiffies = jiffies; - resultsize = i; - return i; - } - if (status == (STATUS_DIR | STATUS_READY | STATUS_BUSY)) - reply_buffer[i] = fd_inb(FD_DATA); - else - break; - } - if (initialized) { - DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n", - fdc, status, i); - show_floppy(); - } - FDCS->reset = 1; - return -1; -} - -#define MORE_OUTPUT -2 -/* does the fdc need more output? */ -static int need_more_output(void) -{ - int status = wait_til_ready(); - - if (status < 0) - return -1; - - if (is_ready_state(status)) - return MORE_OUTPUT; - - return result(); -} - -/* Set perpendicular mode as required, based on data rate, if supported. - * 82077 Now tested. 1Mbps data rate only possible with 82077-1. - */ -static void perpendicular_mode(void) -{ - unsigned char perp_mode; - - if (raw_cmd->rate & 0x40) { - switch (raw_cmd->rate & 3) { - case 0: - perp_mode = 2; - break; - case 3: - perp_mode = 3; - break; - default: - DPRINT("Invalid data rate for perpendicular mode!\n"); - cont->done(0); - FDCS->reset = 1; - /* - * convenient way to return to - * redo without too much hassle - * (deep stack et al.) - */ - return; - } - } else - perp_mode = 0; - - if (FDCS->perp_mode == perp_mode) - return; - if (FDCS->version >= FDC_82077_ORIG) { - output_byte(FD_PERPENDICULAR); - output_byte(perp_mode); - FDCS->perp_mode = perp_mode; - } else if (perp_mode) { - DPRINT("perpendicular mode not supported by this FDC.\n"); - } -} /* perpendicular_mode */ - -static int fifo_depth = 0xa; -static int no_fifo; - -static int fdc_configure(void) -{ - /* Turn on FIFO */ - output_byte(FD_CONFIGURE); - if (need_more_output() != MORE_OUTPUT) - return 0; - output_byte(0); - output_byte(0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf)); - output_byte(0); /* pre-compensation from track - 0 upwards */ - return 1; -} - -#define NOMINAL_DTR 500 - -/* Issue a "SPECIFY" command to set the step rate time, head unload time, - * head load time, and DMA disable flag to values needed by floppy. - * - * The value "dtr" is the data transfer rate in Kbps. It is needed - * to account for the data rate-based scaling done by the 82072 and 82077 - * FDC types. This parameter is ignored for other types of FDCs (i.e. - * 8272a). - * - * Note that changing the data transfer rate has a (probably deleterious) - * effect on the parameters subject to scaling for 82072/82077 FDCs, so - * fdc_specify is called again after each data transfer rate - * change. - * - * srt: 1000 to 16000 in microseconds - * hut: 16 to 240 milliseconds - * hlt: 2 to 254 milliseconds - * - * These values are rounded up to the next highest available delay time. - */ -static void fdc_specify(void) -{ - unsigned char spec1; - unsigned char spec2; - unsigned long srt; - unsigned long hlt; - unsigned long hut; - unsigned long dtr = NOMINAL_DTR; - unsigned long scale_dtr = NOMINAL_DTR; - int hlt_max_code = 0x7f; - int hut_max_code = 0xf; - - if (FDCS->need_configure && FDCS->version >= FDC_82072A) { - fdc_configure(); - FDCS->need_configure = 0; - } - - switch (raw_cmd->rate & 0x03) { - case 3: - dtr = 1000; - break; - case 1: - dtr = 300; - if (FDCS->version >= FDC_82078) { - /* chose the default rate table, not the one - * where 1 = 2 Mbps */ - output_byte(FD_DRIVESPEC); - if (need_more_output() == MORE_OUTPUT) { - output_byte(UNIT(current_drive)); - output_byte(0xc0); - } - } - break; - case 2: - dtr = 250; - break; - } - - if (FDCS->version >= FDC_82072) { - scale_dtr = dtr; - hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */ - hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */ - } - - /* Convert step rate from microseconds to milliseconds and 4 bits */ - srt = 16 - DIV_ROUND_UP(DP->srt * scale_dtr / 1000, NOMINAL_DTR); - if (slow_floppy) - srt = srt / 4; - - SUPBOUND(srt, 0xf); - INFBOUND(srt, 0); - - hlt = DIV_ROUND_UP(DP->hlt * scale_dtr / 2, NOMINAL_DTR); - if (hlt < 0x01) - hlt = 0x01; - else if (hlt > 0x7f) - hlt = hlt_max_code; - - hut = DIV_ROUND_UP(DP->hut * scale_dtr / 16, NOMINAL_DTR); - if (hut < 0x1) - hut = 0x1; - else if (hut > 0xf) - hut = hut_max_code; - - spec1 = (srt << 4) | hut; - spec2 = (hlt << 1) | (use_virtual_dma & 1); - - /* If these parameters did not change, just return with success */ - if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) { - /* Go ahead and set spec1 and spec2 */ - output_byte(FD_SPECIFY); - output_byte(FDCS->spec1 = spec1); - output_byte(FDCS->spec2 = spec2); - } -} /* fdc_specify */ - -/* Set the FDC's data transfer rate on behalf of the specified drive. - * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue - * of the specify command (i.e. using the fdc_specify function). - */ -static int fdc_dtr(void) -{ - /* If data rate not already set to desired value, set it. */ - if ((raw_cmd->rate & 3) == FDCS->dtr) - return 0; - - /* Set dtr */ - fd_outb(raw_cmd->rate & 3, FD_DCR); - - /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB) - * need a stabilization period of several milliseconds to be - * enforced after data rate changes before R/W operations. - * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies) - */ - FDCS->dtr = raw_cmd->rate & 3; - return fd_wait_for_completion(jiffies + 2UL * HZ / 100, floppy_ready); -} /* fdc_dtr */ - -static void tell_sector(void) -{ - pr_cont(": track %d, head %d, sector %d, size %d", - R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE); -} /* tell_sector */ - -static void print_errors(void) -{ - DPRINT(""); - if (ST0 & ST0_ECE) { - pr_cont("Recalibrate failed!"); - } else if (ST2 & ST2_CRC) { - pr_cont("data CRC error"); - tell_sector(); - } else if (ST1 & ST1_CRC) { - pr_cont("CRC error"); - tell_sector(); - } else if ((ST1 & (ST1_MAM | ST1_ND)) || - (ST2 & ST2_MAM)) { - if (!probing) { - pr_cont("sector not found"); - tell_sector(); - } else - pr_cont("probe failed..."); - } else if (ST2 & ST2_WC) { /* seek error */ - pr_cont("wrong cylinder"); - } else if (ST2 & ST2_BC) { /* cylinder marked as bad */ - pr_cont("bad cylinder"); - } else { - pr_cont("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", - ST0, ST1, ST2); - tell_sector(); - } - pr_cont("\n"); -} - -/* - * OK, this error interpreting routine is called after a - * DMA read/write has succeeded - * or failed, so we check the results, and copy any buffers. - * hhb: Added better error reporting. - * ak: Made this into a separate routine. - */ -static int interpret_errors(void) -{ - char bad; - - if (inr != 7) { - DPRINT("-- FDC reply error\n"); - FDCS->reset = 1; - return 1; - } - - /* check IC to find cause of interrupt */ - switch (ST0 & ST0_INTR) { - case 0x40: /* error occurred during command execution */ - if (ST1 & ST1_EOC) - return 0; /* occurs with pseudo-DMA */ - bad = 1; - if (ST1 & ST1_WP) { - DPRINT("Drive is write protected\n"); - clear_bit(FD_DISK_WRITABLE_BIT, &DRS->flags); - cont->done(0); - bad = 2; - } else if (ST1 & ST1_ND) { - set_bit(FD_NEED_TWADDLE_BIT, &DRS->flags); - } else if (ST1 & ST1_OR) { - if (DP->flags & FTD_MSG) - DPRINT("Over/Underrun - retrying\n"); - bad = 0; - } else if (*errors >= DP->max_errors.reporting) { - print_errors(); - } - if (ST2 & ST2_WC || ST2 & ST2_BC) - /* wrong cylinder => recal */ - DRS->track = NEED_2_RECAL; - return bad; - case 0x80: /* invalid command given */ - DPRINT("Invalid FDC command given!\n"); - cont->done(0); - return 2; - case 0xc0: - DPRINT("Abnormal termination caused by polling\n"); - cont->error(); - return 2; - default: /* (0) Normal command termination */ - return 0; - } -} - -/* - * This routine is called when everything should be correctly set up - * for the transfer (i.e. floppy motor is on, the correct floppy is - * selected, and the head is sitting on the right track). - */ -static void setup_rw_floppy(void) -{ - int i; - int r; - int flags; - unsigned long ready_date; - void (*function)(void); - - flags = raw_cmd->flags; - if (flags & (FD_RAW_READ | FD_RAW_WRITE)) - flags |= FD_RAW_INTR; - - if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)) { - ready_date = DRS->spinup_date + DP->spinup; - /* If spinup will take a long time, rerun scandrives - * again just before spinup completion. Beware that - * after scandrives, we must again wait for selection. - */ - if (time_after(ready_date, jiffies + DP->select_delay)) { - ready_date -= DP->select_delay; - function = floppy_start; - } else - function = setup_rw_floppy; - - /* wait until the floppy is spinning fast enough */ - if (fd_wait_for_completion(ready_date, function)) - return; - } - if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE)) - setup_DMA(); - - if (flags & FD_RAW_INTR) - do_floppy = main_command_interrupt; - - r = 0; - for (i = 0; i < raw_cmd->cmd_count; i++) - r |= output_byte(raw_cmd->cmd[i]); - - debugt(__func__, "rw_command"); - - if (r) { - cont->error(); - reset_fdc(); - return; - } - - if (!(flags & FD_RAW_INTR)) { - inr = result(); - cont->interrupt(); - } else if (flags & FD_RAW_NEED_DISK) - fd_watchdog(); -} - -static int blind_seek; - -/* - * This is the routine called after every seek (or recalibrate) interrupt - * from the floppy controller. - */ -static void seek_interrupt(void) -{ - debugt(__func__, ""); - if (inr != 2 || (ST0 & 0xF8) != 0x20) { - DPRINT("seek failed\n"); - DRS->track = NEED_2_RECAL; - cont->error(); - cont->redo(); - return; - } - if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek) { - debug_dcl(DP->flags, - "clearing NEWCHANGE flag because of effective seek\n"); - debug_dcl(DP->flags, "jiffies=%lu\n", jiffies); - clear_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags); - /* effective seek */ - DRS->select_date = jiffies; - } - DRS->track = ST1; - floppy_ready(); -} - -static void check_wp(void) -{ - if (test_bit(FD_VERIFY_BIT, &DRS->flags)) { - /* check write protection */ - output_byte(FD_GETSTATUS); - output_byte(UNIT(current_drive)); - if (result() != 1) { - FDCS->reset = 1; - return; - } - clear_bit(FD_VERIFY_BIT, &DRS->flags); - clear_bit(FD_NEED_TWADDLE_BIT, &DRS->flags); - debug_dcl(DP->flags, - "checking whether disk is write protected\n"); - debug_dcl(DP->flags, "wp=%x\n", ST3 & 0x40); - if (!(ST3 & 0x40)) - set_bit(FD_DISK_WRITABLE_BIT, &DRS->flags); - else - clear_bit(FD_DISK_WRITABLE_BIT, &DRS->flags); - } -} - -static void seek_floppy(void) -{ - int track; - - blind_seek = 0; - - debug_dcl(DP->flags, "calling disk change from %s\n", __func__); - - if (!test_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags) && - disk_change(current_drive) && (raw_cmd->flags & FD_RAW_NEED_DISK)) { - /* the media changed flag should be cleared after the seek. - * If it isn't, this means that there is really no disk in - * the drive. - */ - set_bit(FD_DISK_CHANGED_BIT, &DRS->flags); - cont->done(0); - cont->redo(); - return; - } - if (DRS->track <= NEED_1_RECAL) { - recalibrate_floppy(); - return; - } else if (test_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags) && - (raw_cmd->flags & FD_RAW_NEED_DISK) && - (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) { - /* we seek to clear the media-changed condition. Does anybody - * know a more elegant way, which works on all drives? */ - if (raw_cmd->track) - track = raw_cmd->track - 1; - else { - if (DP->flags & FD_SILENT_DCL_CLEAR) { - set_dor(fdc, ~(0x10 << UNIT(current_drive)), 0); - blind_seek = 1; - raw_cmd->flags |= FD_RAW_NEED_SEEK; - } - track = 1; - } - } else { - check_wp(); - if (raw_cmd->track != DRS->track && - (raw_cmd->flags & FD_RAW_NEED_SEEK)) - track = raw_cmd->track; - else { - setup_rw_floppy(); - return; - } - } - - do_floppy = seek_interrupt; - output_byte(FD_SEEK); - output_byte(UNIT(current_drive)); - if (output_byte(track) < 0) { - reset_fdc(); - return; - } - debugt(__func__, ""); -} - -static void recal_interrupt(void) -{ - debugt(__func__, ""); - if (inr != 2) - FDCS->reset = 1; - else if (ST0 & ST0_ECE) { - switch (DRS->track) { - case NEED_1_RECAL: - debugt(__func__, "need 1 recal"); - /* after a second recalibrate, we still haven't - * reached track 0. Probably no drive. Raise an - * error, as failing immediately might upset - * computers possessed by the Devil :-) */ - cont->error(); - cont->redo(); - return; - case NEED_2_RECAL: - debugt(__func__, "need 2 recal"); - /* If we already did a recalibrate, - * and we are not at track 0, this - * means we have moved. (The only way - * not to move at recalibration is to - * be already at track 0.) Clear the - * new change flag */ - debug_dcl(DP->flags, - "clearing NEWCHANGE flag because of second recalibrate\n"); - - clear_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags); - DRS->select_date = jiffies; - /* fall through */ - default: - debugt(__func__, "default"); - /* Recalibrate moves the head by at - * most 80 steps. If after one - * recalibrate we don't have reached - * track 0, this might mean that we - * started beyond track 80. Try - * again. */ - DRS->track = NEED_1_RECAL; - break; - } - } else - DRS->track = ST1; - floppy_ready(); -} - -static void print_result(char *message, int inr) -{ - int i; - - DPRINT("%s ", message); - if (inr >= 0) - for (i = 0; i < inr; i++) - pr_cont("repl[%d]=%x ", i, reply_buffer[i]); - pr_cont("\n"); -} - -/* interrupt handler. Note that this can be called externally on the Sparc */ -irqreturn_t floppy_interrupt(int irq, void *dev_id) -{ - int do_print; - unsigned long f; - void (*handler)(void) = do_floppy; - - lasthandler = handler; - interruptjiffies = jiffies; - - f = claim_dma_lock(); - fd_disable_dma(); - release_dma_lock(f); - - do_floppy = NULL; - if (fdc >= N_FDC || FDCS->address == -1) { - /* we don't even know which FDC is the culprit */ - pr_info("DOR0=%x\n", fdc_state[0].dor); - pr_info("floppy interrupt on bizarre fdc %d\n", fdc); - pr_info("handler=%ps\n", handler); - is_alive(__func__, "bizarre fdc"); - return IRQ_NONE; - } - - FDCS->reset = 0; - /* We have to clear the reset flag here, because apparently on boxes - * with level triggered interrupts (PS/2, Sparc, ...), it is needed to - * emit SENSEI's to clear the interrupt line. And FDCS->reset blocks the - * emission of the SENSEI's. - * It is OK to emit floppy commands because we are in an interrupt - * handler here, and thus we have to fear no interference of other - * activity. - */ - - do_print = !handler && print_unex && initialized; - - inr = result(); - if (do_print) - print_result("unexpected interrupt", inr); - if (inr == 0) { - int max_sensei = 4; - do { - output_byte(FD_SENSEI); - inr = result(); - if (do_print) - print_result("sensei", inr); - max_sensei--; - } while ((ST0 & 0x83) != UNIT(current_drive) && - inr == 2 && max_sensei); - } - if (!handler) { - FDCS->reset = 1; - return IRQ_NONE; - } - schedule_bh(handler); - is_alive(__func__, "normal interrupt end"); - - /* FIXME! Was it really for us? */ - return IRQ_HANDLED; -} - -static void recalibrate_floppy(void) -{ - debugt(__func__, ""); - do_floppy = recal_interrupt; - output_byte(FD_RECALIBRATE); - if (output_byte(UNIT(current_drive)) < 0) - reset_fdc(); -} - -/* - * Must do 4 FD_SENSEIs after reset because of ``drive polling''. - */ -static void reset_interrupt(void) -{ - debugt(__func__, ""); - result(); /* get the status ready for set_fdc */ - if (FDCS->reset) { - pr_info("reset set in interrupt, calling %ps\n", cont->error); - cont->error(); /* a reset just after a reset. BAD! */ - } - cont->redo(); -} - -/* - * reset is done by pulling bit 2 of DOR low for a while (old FDCs), - * or by setting the self clearing bit 7 of STATUS (newer FDCs) - */ -static void reset_fdc(void) -{ - unsigned long flags; - - do_floppy = reset_interrupt; - FDCS->reset = 0; - reset_fdc_info(0); - - /* Pseudo-DMA may intercept 'reset finished' interrupt. */ - /* Irrelevant for systems with true DMA (i386). */ - - flags = claim_dma_lock(); - fd_disable_dma(); - release_dma_lock(flags); - - if (FDCS->version >= FDC_82072A) - fd_outb(0x80 | (FDCS->dtr & 3), FD_STATUS); - else { - fd_outb(FDCS->dor & ~0x04, FD_DOR); - udelay(FD_RESET_DELAY); - fd_outb(FDCS->dor, FD_DOR); - } -} - -static void show_floppy(void) -{ - int i; - - pr_info("\n"); - pr_info("floppy driver state\n"); - pr_info("-------------------\n"); - pr_info("now=%lu last interrupt=%lu diff=%lu last called handler=%ps\n", - jiffies, interruptjiffies, jiffies - interruptjiffies, - lasthandler); - - pr_info("timeout_message=%s\n", timeout_message); - pr_info("last output bytes:\n"); - for (i = 0; i < OLOGSIZE; i++) - pr_info("%2x %2x %lu\n", - output_log[(i + output_log_pos) % OLOGSIZE].data, - output_log[(i + output_log_pos) % OLOGSIZE].status, - output_log[(i + output_log_pos) % OLOGSIZE].jiffies); - pr_info("last result at %lu\n", resultjiffies); - pr_info("last redo_fd_request at %lu\n", lastredo); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, - reply_buffer, resultsize, true); - - pr_info("status=%x\n", fd_inb(FD_STATUS)); - pr_info("fdc_busy=%lu\n", fdc_busy); - if (do_floppy) - pr_info("do_floppy=%ps\n", do_floppy); - if (work_pending(&floppy_work)) - pr_info("floppy_work.func=%ps\n", floppy_work.func); - if (delayed_work_pending(&fd_timer)) - pr_info("delayed work.function=%p expires=%ld\n", - fd_timer.work.func, - fd_timer.timer.expires - jiffies); - if (delayed_work_pending(&fd_timeout)) - pr_info("timer_function=%p expires=%ld\n", - fd_timeout.work.func, - fd_timeout.timer.expires - jiffies); - - pr_info("cont=%p\n", cont); - pr_info("current_req=%p\n", current_req); - pr_info("command_status=%d\n", command_status); - pr_info("\n"); -} - -static void floppy_shutdown(struct work_struct *arg) -{ - unsigned long flags; - - if (initialized) - show_floppy(); - cancel_activity(); - - flags = claim_dma_lock(); - fd_disable_dma(); - release_dma_lock(flags); - - /* avoid dma going to a random drive after shutdown */ - - if (initialized) - DPRINT("floppy timeout called\n"); - FDCS->reset = 1; - if (cont) { - cont->done(0); - cont->redo(); /* this will recall reset when needed */ - } else { - pr_info("no cont in shutdown!\n"); - process_fd_request(); - } - is_alive(__func__, ""); -} - -/* start motor, check media-changed condition and write protection */ -static int start_motor(void (*function)(void)) -{ - int mask; - int data; - - mask = 0xfc; - data = UNIT(current_drive); - if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)) { - if (!(FDCS->dor & (0x10 << UNIT(current_drive)))) { - set_debugt(); - /* no read since this drive is running */ - DRS->first_read_date = 0; - /* note motor start time if motor is not yet running */ - DRS->spinup_date = jiffies; - data |= (0x10 << UNIT(current_drive)); - } - } else if (FDCS->dor & (0x10 << UNIT(current_drive))) - mask &= ~(0x10 << UNIT(current_drive)); - - /* starts motor and selects floppy */ - del_timer(motor_off_timer + current_drive); - set_dor(fdc, mask, data); - - /* wait_for_completion also schedules reset if needed. */ - return fd_wait_for_completion(DRS->select_date + DP->select_delay, - function); -} - -static void floppy_ready(void) -{ - if (FDCS->reset) { - reset_fdc(); - return; - } - if (start_motor(floppy_ready)) - return; - if (fdc_dtr()) - return; - - debug_dcl(DP->flags, "calling disk change from floppy_ready\n"); - if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) && - disk_change(current_drive) && !DP->select_delay) - twaddle(); /* this clears the dcl on certain - * drive/controller combinations */ - -#ifdef fd_chose_dma_mode - if ((raw_cmd->flags & FD_RAW_READ) || (raw_cmd->flags & FD_RAW_WRITE)) { - unsigned long flags = claim_dma_lock(); - fd_chose_dma_mode(raw_cmd->kernel_data, raw_cmd->length); - release_dma_lock(flags); - } -#endif - - if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)) { - perpendicular_mode(); - fdc_specify(); /* must be done here because of hut, hlt ... */ - seek_floppy(); - } else { - if ((raw_cmd->flags & FD_RAW_READ) || - (raw_cmd->flags & FD_RAW_WRITE)) - fdc_specify(); - setup_rw_floppy(); - } -} - -static void floppy_start(void) -{ - reschedule_timeout(current_reqD, "floppy start"); - - scandrives(); - debug_dcl(DP->flags, "setting NEWCHANGE in floppy_start\n"); - set_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags); - floppy_ready(); -} - -/* - * ======================================================================== - * here ends the bottom half. Exported routines are: - * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc, - * start_motor, reset_fdc, reset_fdc_info, interpret_errors. - * Initialization also uses output_byte, result, set_dor, floppy_interrupt - * and set_dor. - * ======================================================================== - */ -/* - * General purpose continuations. - * ============================== - */ - -static void do_wakeup(void) -{ - reschedule_timeout(MAXTIMEOUT, "do wakeup"); - cont = NULL; - command_status += 2; - wake_up(&command_done); -} - -static const struct cont_t wakeup_cont = { - .interrupt = empty, - .redo = do_wakeup, - .error = empty, - .done = (done_f)empty -}; - -static const struct cont_t intr_cont = { - .interrupt = empty, - .redo = process_fd_request, - .error = empty, - .done = (done_f)empty -}; - -static int wait_til_done(void (*handler)(void), bool interruptible) -{ - int ret; - - schedule_bh(handler); - - if (interruptible) - wait_event_interruptible(command_done, command_status >= 2); - else - wait_event(command_done, command_status >= 2); - - if (command_status < 2) { - cancel_activity(); - cont = &intr_cont; - reset_fdc(); - return -EINTR; - } - - if (FDCS->reset) - command_status = FD_COMMAND_ERROR; - if (command_status == FD_COMMAND_OKAY) - ret = 0; - else - ret = -EIO; - command_status = FD_COMMAND_NONE; - return ret; -} - -static void generic_done(int result) -{ - command_status = result; - cont = &wakeup_cont; -} - -static void generic_success(void) -{ - cont->done(1); -} - -static void generic_failure(void) -{ - cont->done(0); -} - -static void success_and_wakeup(void) -{ - generic_success(); - cont->redo(); -} - -/* - * formatting and rw support. - * ========================== - */ - -static int next_valid_format(void) -{ - int probed_format; - - probed_format = DRS->probed_format; - while (1) { - if (probed_format >= 8 || !DP->autodetect[probed_format]) { - DRS->probed_format = 0; - return 1; - } - if (floppy_type[DP->autodetect[probed_format]].sect) { - DRS->probed_format = probed_format; - return 0; - } - probed_format++; - } -} - -static void bad_flp_intr(void) -{ - int err_count; - - if (probing) { - DRS->probed_format++; - if (!next_valid_format()) - return; - } - err_count = ++(*errors); - INFBOUND(DRWE->badness, err_count); - if (err_count > DP->max_errors.abort) - cont->done(0); - if (err_count > DP->max_errors.reset) - FDCS->reset = 1; - else if (err_count > DP->max_errors.recal) - DRS->track = NEED_2_RECAL; -} - -static void set_floppy(int drive) -{ - int type = ITYPE(UDRS->fd_device); - - if (type) - _floppy = floppy_type + type; - else - _floppy = current_type[drive]; -} - -/* - * formatting support. - * =================== - */ -static void format_interrupt(void) -{ - switch (interpret_errors()) { - case 1: - cont->error(); - case 2: - break; - case 0: - cont->done(1); - } - cont->redo(); -} - -#define FM_MODE(x, y) ((y) & ~(((x)->rate & 0x80) >> 1)) -#define CT(x) ((x) | 0xc0) - -static void setup_format_params(int track) -{ - int n; - int il; - int count; - int head_shift; - int track_shift; - struct fparm { - unsigned char track, head, sect, size; - } *here = (struct fparm *)floppy_track_buffer; - - raw_cmd = &default_raw_cmd; - raw_cmd->track = track; - - raw_cmd->flags = (FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN | - FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK); - raw_cmd->rate = _floppy->rate & 0x43; - raw_cmd->cmd_count = NR_F; - COMMAND = FM_MODE(_floppy, FD_FORMAT); - DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy, format_req.head); - F_SIZECODE = FD_SIZECODE(_floppy); - F_SECT_PER_TRACK = _floppy->sect << 2 >> F_SIZECODE; - F_GAP = _floppy->fmt_gap; - F_FILL = FD_FILL_BYTE; - - raw_cmd->kernel_data = floppy_track_buffer; - raw_cmd->length = 4 * F_SECT_PER_TRACK; - - /* allow for about 30ms for data transport per track */ - head_shift = (F_SECT_PER_TRACK + 5) / 6; - - /* a ``cylinder'' is two tracks plus a little stepping time */ - track_shift = 2 * head_shift + 3; - - /* position of logical sector 1 on this track */ - n = (track_shift * format_req.track + head_shift * format_req.head) - % F_SECT_PER_TRACK; - - /* determine interleave */ - il = 1; - if (_floppy->fmt_gap < 0x22) - il++; - - /* initialize field */ - for (count = 0; count < F_SECT_PER_TRACK; ++count) { - here[count].track = format_req.track; - here[count].head = format_req.head; - here[count].sect = 0; - here[count].size = F_SIZECODE; - } - /* place logical sectors */ - for (count = 1; count <= F_SECT_PER_TRACK; ++count) { - here[n].sect = count; - n = (n + il) % F_SECT_PER_TRACK; - if (here[n].sect) { /* sector busy, find next free sector */ - ++n; - if (n >= F_SECT_PER_TRACK) { - n -= F_SECT_PER_TRACK; - while (here[n].sect) - ++n; - } - } - } - if (_floppy->stretch & FD_SECTBASEMASK) { - for (count = 0; count < F_SECT_PER_TRACK; count++) - here[count].sect += FD_SECTBASE(_floppy) - 1; - } -} - -static void redo_format(void) -{ - buffer_track = -1; - setup_format_params(format_req.track << STRETCH(_floppy)); - floppy_start(); - debugt(__func__, "queue format request"); -} - -static const struct cont_t format_cont = { - .interrupt = format_interrupt, - .redo = redo_format, - .error = bad_flp_intr, - .done = generic_done -}; - -static int do_format(int drive, struct format_descr *tmp_format_req) -{ - int ret; - - if (lock_fdc(drive)) - return -EINTR; - - set_floppy(drive); - if (!_floppy || - _floppy->track > DP->tracks || - tmp_format_req->track >= _floppy->track || - tmp_format_req->head >= _floppy->head || - (_floppy->sect << 2) % (1 << FD_SIZECODE(_floppy)) || - !_floppy->fmt_gap) { - process_fd_request(); - return -EINVAL; - } - format_req = *tmp_format_req; - format_errors = 0; - cont = &format_cont; - errors = &format_errors; - ret = wait_til_done(redo_format, true); - if (ret == -EINTR) - return -EINTR; - process_fd_request(); - return ret; -} - -/* - * Buffer read/write and support - * ============================= - */ - -static void floppy_end_request(struct request *req, blk_status_t error) -{ - unsigned int nr_sectors = current_count_sectors; - unsigned int drive = (unsigned long)req->rq_disk->private_data; - - /* current_count_sectors can be zero if transfer failed */ - if (error) - nr_sectors = blk_rq_cur_sectors(req); - if (blk_update_request(req, error, nr_sectors << 9)) - return; - __blk_mq_end_request(req, error); - - /* We're done with the request */ - floppy_off(drive); - current_req = NULL; -} - -/* new request_done. Can handle physical sectors which are smaller than a - * logical buffer */ -static void request_done(int uptodate) -{ - struct request *req = current_req; - int block; - char msg[sizeof("request done ") + sizeof(int) * 3]; - - probing = 0; - snprintf(msg, sizeof(msg), "request done %d", uptodate); - reschedule_timeout(MAXTIMEOUT, msg); - - if (!req) { - pr_info("floppy.c: no request in request_done\n"); - return; - } - - if (uptodate) { - /* maintain values for invalidation on geometry - * change */ - block = current_count_sectors + blk_rq_pos(req); - INFBOUND(DRS->maxblock, block); - if (block > _floppy->sect) - DRS->maxtrack = 1; - - floppy_end_request(req, 0); - } else { - if (rq_data_dir(req) == WRITE) { - /* record write error information */ - DRWE->write_errors++; - if (DRWE->write_errors == 1) { - DRWE->first_error_sector = blk_rq_pos(req); - DRWE->first_error_generation = DRS->generation; - } - DRWE->last_error_sector = blk_rq_pos(req); - DRWE->last_error_generation = DRS->generation; - } - floppy_end_request(req, BLK_STS_IOERR); - } -} - -/* Interrupt handler evaluating the result of the r/w operation */ -static void rw_interrupt(void) -{ - int eoc; - int ssize; - int heads; - int nr_sectors; - - if (R_HEAD >= 2) { - /* some Toshiba floppy controllers occasionnally seem to - * return bogus interrupts after read/write operations, which - * can be recognized by a bad head number (>= 2) */ - return; - } - - if (!DRS->first_read_date) - DRS->first_read_date = jiffies; - - nr_sectors = 0; - ssize = DIV_ROUND_UP(1 << SIZECODE, 4); - - if (ST1 & ST1_EOC) - eoc = 1; - else - eoc = 0; - - if (COMMAND & 0x80) - heads = 2; - else - heads = 1; - - nr_sectors = (((R_TRACK - TRACK) * heads + - R_HEAD - HEAD) * SECT_PER_TRACK + - R_SECTOR - SECTOR + eoc) << SIZECODE >> 2; - - if (nr_sectors / ssize > - DIV_ROUND_UP(in_sector_offset + current_count_sectors, ssize)) { - DPRINT("long rw: %x instead of %lx\n", - nr_sectors, current_count_sectors); - pr_info("rs=%d s=%d\n", R_SECTOR, SECTOR); - pr_info("rh=%d h=%d\n", R_HEAD, HEAD); - pr_info("rt=%d t=%d\n", R_TRACK, TRACK); - pr_info("heads=%d eoc=%d\n", heads, eoc); - pr_info("spt=%d st=%d ss=%d\n", - SECT_PER_TRACK, fsector_t, ssize); - pr_info("in_sector_offset=%d\n", in_sector_offset); - } - - nr_sectors -= in_sector_offset; - INFBOUND(nr_sectors, 0); - SUPBOUND(current_count_sectors, nr_sectors); - - switch (interpret_errors()) { - case 2: - cont->redo(); - return; - case 1: - if (!current_count_sectors) { - cont->error(); - cont->redo(); - return; - } - break; - case 0: - if (!current_count_sectors) { - cont->redo(); - return; - } - current_type[current_drive] = _floppy; - floppy_sizes[TOMINOR(current_drive)] = _floppy->size; - break; - } - - if (probing) { - if (DP->flags & FTD_MSG) - DPRINT("Auto-detected floppy type %s in fd%d\n", - _floppy->name, current_drive); - current_type[current_drive] = _floppy; - floppy_sizes[TOMINOR(current_drive)] = _floppy->size; - probing = 0; - } - - if (CT(COMMAND) != FD_READ || - raw_cmd->kernel_data == bio_data(current_req->bio)) { - /* transfer directly from buffer */ - cont->done(1); - } else if (CT(COMMAND) == FD_READ) { - buffer_track = raw_cmd->track; - buffer_drive = current_drive; - INFBOUND(buffer_max, nr_sectors + fsector_t); - } - cont->redo(); -} - -/* Compute maximal contiguous buffer size. */ -static int buffer_chain_size(void) -{ - struct bio_vec bv; - int size; - struct req_iterator iter; - char *base; - - base = bio_data(current_req->bio); - size = 0; - - rq_for_each_segment(bv, current_req, iter) { - if (page_address(bv.bv_page) + bv.bv_offset != base + size) - break; - - size += bv.bv_len; - } - - return size >> 9; -} - -/* Compute the maximal transfer size */ -static int transfer_size(int ssize, int max_sector, int max_size) -{ - SUPBOUND(max_sector, fsector_t + max_size); - - /* alignment */ - max_sector -= (max_sector % _floppy->sect) % ssize; - - /* transfer size, beginning not aligned */ - current_count_sectors = max_sector - fsector_t; - - return max_sector; -} - -/* - * Move data from/to the track buffer to/from the buffer cache. - */ -static void copy_buffer(int ssize, int max_sector, int max_sector_2) -{ - int remaining; /* number of transferred 512-byte sectors */ - struct bio_vec bv; - char *buffer; - char *dma_buffer; - int size; - struct req_iterator iter; - - max_sector = transfer_size(ssize, - min(max_sector, max_sector_2), - blk_rq_sectors(current_req)); - - if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE && - buffer_max > fsector_t + blk_rq_sectors(current_req)) - current_count_sectors = min_t(int, buffer_max - fsector_t, - blk_rq_sectors(current_req)); - - remaining = current_count_sectors << 9; - if (remaining > blk_rq_bytes(current_req) && CT(COMMAND) == FD_WRITE) { - DPRINT("in copy buffer\n"); - pr_info("current_count_sectors=%ld\n", current_count_sectors); - pr_info("remaining=%d\n", remaining >> 9); - pr_info("current_req->nr_sectors=%u\n", - blk_rq_sectors(current_req)); - pr_info("current_req->current_nr_sectors=%u\n", - blk_rq_cur_sectors(current_req)); - pr_info("max_sector=%d\n", max_sector); - pr_info("ssize=%d\n", ssize); - } - - buffer_max = max(max_sector, buffer_max); - - dma_buffer = floppy_track_buffer + ((fsector_t - buffer_min) << 9); - - size = blk_rq_cur_bytes(current_req); - - rq_for_each_segment(bv, current_req, iter) { - if (!remaining) - break; - - size = bv.bv_len; - SUPBOUND(size, remaining); - - buffer = page_address(bv.bv_page) + bv.bv_offset; - if (dma_buffer + size > - floppy_track_buffer + (max_buffer_sectors << 10) || - dma_buffer < floppy_track_buffer) { - DPRINT("buffer overrun in copy buffer %d\n", - (int)((floppy_track_buffer - dma_buffer) >> 9)); - pr_info("fsector_t=%d buffer_min=%d\n", - fsector_t, buffer_min); - pr_info("current_count_sectors=%ld\n", - current_count_sectors); - if (CT(COMMAND) == FD_READ) - pr_info("read\n"); - if (CT(COMMAND) == FD_WRITE) - pr_info("write\n"); - break; - } - if (((unsigned long)buffer) % 512) - DPRINT("%p buffer not aligned\n", buffer); - - if (CT(COMMAND) == FD_READ) - memcpy(buffer, dma_buffer, size); - else - memcpy(dma_buffer, buffer, size); - - remaining -= size; - dma_buffer += size; - } - if (remaining) { - if (remaining > 0) - max_sector -= remaining >> 9; - DPRINT("weirdness: remaining %d\n", remaining >> 9); - } -} - -/* work around a bug in pseudo DMA - * (on some FDCs) pseudo DMA does not stop when the CPU stops - * sending data. Hence we need a different way to signal the - * transfer length: We use SECT_PER_TRACK. Unfortunately, this - * does not work with MT, hence we can only transfer one head at - * a time - */ -static void virtualdmabug_workaround(void) -{ - int hard_sectors; - int end_sector; - - if (CT(COMMAND) == FD_WRITE) { - COMMAND &= ~0x80; /* switch off multiple track mode */ - - hard_sectors = raw_cmd->length >> (7 + SIZECODE); - end_sector = SECTOR + hard_sectors - 1; - if (end_sector > SECT_PER_TRACK) { - pr_info("too many sectors %d > %d\n", - end_sector, SECT_PER_TRACK); - return; - } - SECT_PER_TRACK = end_sector; - /* make sure SECT_PER_TRACK - * points to end of transfer */ - } -} - -/* - * Formulate a read/write request. - * this routine decides where to load the data (directly to buffer, or to - * tmp floppy area), how much data to load (the size of the buffer, the whole - * track, or a single sector) - * All floppy_track_buffer handling goes in here. If we ever add track buffer - * allocation on the fly, it should be done here. No other part should need - * modification. - */ - -static int make_raw_rw_request(void) -{ - int aligned_sector_t; - int max_sector; - int max_size; - int tracksize; - int ssize; - - if (WARN(max_buffer_sectors == 0, "VFS: Block I/O scheduled on unopened device\n")) - return 0; - - set_fdc((long)current_req->rq_disk->private_data); - - raw_cmd = &default_raw_cmd; - raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK; - raw_cmd->cmd_count = NR_RW; - if (rq_data_dir(current_req) == READ) { - raw_cmd->flags |= FD_RAW_READ; - COMMAND = FM_MODE(_floppy, FD_READ); - } else if (rq_data_dir(current_req) == WRITE) { - raw_cmd->flags |= FD_RAW_WRITE; - COMMAND = FM_MODE(_floppy, FD_WRITE); - } else { - DPRINT("%s: unknown command\n", __func__); - return 0; - } - - max_sector = _floppy->sect * _floppy->head; - - TRACK = (int)blk_rq_pos(current_req) / max_sector; - fsector_t = (int)blk_rq_pos(current_req) % max_sector; - if (_floppy->track && TRACK >= _floppy->track) { - if (blk_rq_cur_sectors(current_req) & 1) { - current_count_sectors = 1; - return 1; - } else - return 0; - } - HEAD = fsector_t / _floppy->sect; - - if (((_floppy->stretch & (FD_SWAPSIDES | FD_SECTBASEMASK)) || - test_bit(FD_NEED_TWADDLE_BIT, &DRS->flags)) && - fsector_t < _floppy->sect) - max_sector = _floppy->sect; - - /* 2M disks have phantom sectors on the first track */ - if ((_floppy->rate & FD_2M) && (!TRACK) && (!HEAD)) { - max_sector = 2 * _floppy->sect / 3; - if (fsector_t >= max_sector) { - current_count_sectors = - min_t(int, _floppy->sect - fsector_t, - blk_rq_sectors(current_req)); - return 1; - } - SIZECODE = 2; - } else - SIZECODE = FD_SIZECODE(_floppy); - raw_cmd->rate = _floppy->rate & 0x43; - if ((_floppy->rate & FD_2M) && (TRACK || HEAD) && raw_cmd->rate == 2) - raw_cmd->rate = 1; - - if (SIZECODE) - SIZECODE2 = 0xff; - else - SIZECODE2 = 0x80; - raw_cmd->track = TRACK << STRETCH(_floppy); - DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy, HEAD); - GAP = _floppy->gap; - ssize = DIV_ROUND_UP(1 << SIZECODE, 4); - SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE; - SECTOR = ((fsector_t % _floppy->sect) << 2 >> SIZECODE) + - FD_SECTBASE(_floppy); - - /* tracksize describes the size which can be filled up with sectors - * of size ssize. - */ - tracksize = _floppy->sect - _floppy->sect % ssize; - if (tracksize < _floppy->sect) { - SECT_PER_TRACK++; - if (tracksize <= fsector_t % _floppy->sect) - SECTOR--; - - /* if we are beyond tracksize, fill up using smaller sectors */ - while (tracksize <= fsector_t % _floppy->sect) { - while (tracksize + ssize > _floppy->sect) { - SIZECODE--; - ssize >>= 1; - } - SECTOR++; - SECT_PER_TRACK++; - tracksize += ssize; - } - max_sector = HEAD * _floppy->sect + tracksize; - } else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing) { - max_sector = _floppy->sect; - } else if (!HEAD && CT(COMMAND) == FD_WRITE) { - /* for virtual DMA bug workaround */ - max_sector = _floppy->sect; - } - - in_sector_offset = (fsector_t % _floppy->sect) % ssize; - aligned_sector_t = fsector_t - in_sector_offset; - max_size = blk_rq_sectors(current_req); - if ((raw_cmd->track == buffer_track) && - (current_drive == buffer_drive) && - (fsector_t >= buffer_min) && (fsector_t < buffer_max)) { - /* data already in track buffer */ - if (CT(COMMAND) == FD_READ) { - copy_buffer(1, max_sector, buffer_max); - return 1; - } - } else if (in_sector_offset || blk_rq_sectors(current_req) < ssize) { - if (CT(COMMAND) == FD_WRITE) { - unsigned int sectors; - - sectors = fsector_t + blk_rq_sectors(current_req); - if (sectors > ssize && sectors < ssize + ssize) - max_size = ssize + ssize; - else - max_size = ssize; - } - raw_cmd->flags &= ~FD_RAW_WRITE; - raw_cmd->flags |= FD_RAW_READ; - COMMAND = FM_MODE(_floppy, FD_READ); - } else if ((unsigned long)bio_data(current_req->bio) < MAX_DMA_ADDRESS) { - unsigned long dma_limit; - int direct, indirect; - - indirect = - transfer_size(ssize, max_sector, - max_buffer_sectors * 2) - fsector_t; - - /* - * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide - * on a 64 bit machine! - */ - max_size = buffer_chain_size(); - dma_limit = (MAX_DMA_ADDRESS - - ((unsigned long)bio_data(current_req->bio))) >> 9; - if ((unsigned long)max_size > dma_limit) - max_size = dma_limit; - /* 64 kb boundaries */ - if (CROSS_64KB(bio_data(current_req->bio), max_size << 9)) - max_size = (K_64 - - ((unsigned long)bio_data(current_req->bio)) % - K_64) >> 9; - direct = transfer_size(ssize, max_sector, max_size) - fsector_t; - /* - * We try to read tracks, but if we get too many errors, we - * go back to reading just one sector at a time. - * - * This means we should be able to read a sector even if there - * are other bad sectors on this track. - */ - if (!direct || - (indirect * 2 > direct * 3 && - *errors < DP->max_errors.read_track && - ((!probing || - (DP->read_track & (1 << DRS->probed_format)))))) { - max_size = blk_rq_sectors(current_req); - } else { - raw_cmd->kernel_data = bio_data(current_req->bio); - raw_cmd->length = current_count_sectors << 9; - if (raw_cmd->length == 0) { - DPRINT("%s: zero dma transfer attempted\n", __func__); - DPRINT("indirect=%d direct=%d fsector_t=%d\n", - indirect, direct, fsector_t); - return 0; - } - virtualdmabug_workaround(); - return 2; - } - } - - if (CT(COMMAND) == FD_READ) - max_size = max_sector; /* unbounded */ - - /* claim buffer track if needed */ - if (buffer_track != raw_cmd->track || /* bad track */ - buffer_drive != current_drive || /* bad drive */ - fsector_t > buffer_max || - fsector_t < buffer_min || - ((CT(COMMAND) == FD_READ || - (!in_sector_offset && blk_rq_sectors(current_req) >= ssize)) && - max_sector > 2 * max_buffer_sectors + buffer_min && - max_size + fsector_t > 2 * max_buffer_sectors + buffer_min)) { - /* not enough space */ - buffer_track = -1; - buffer_drive = current_drive; - buffer_max = buffer_min = aligned_sector_t; - } - raw_cmd->kernel_data = floppy_track_buffer + - ((aligned_sector_t - buffer_min) << 9); - - if (CT(COMMAND) == FD_WRITE) { - /* copy write buffer to track buffer. - * if we get here, we know that the write - * is either aligned or the data already in the buffer - * (buffer will be overwritten) */ - if (in_sector_offset && buffer_track == -1) - DPRINT("internal error offset !=0 on write\n"); - buffer_track = raw_cmd->track; - buffer_drive = current_drive; - copy_buffer(ssize, max_sector, - 2 * max_buffer_sectors + buffer_min); - } else - transfer_size(ssize, max_sector, - 2 * max_buffer_sectors + buffer_min - - aligned_sector_t); - - /* round up current_count_sectors to get dma xfer size */ - raw_cmd->length = in_sector_offset + current_count_sectors; - raw_cmd->length = ((raw_cmd->length - 1) | (ssize - 1)) + 1; - raw_cmd->length <<= 9; - if ((raw_cmd->length < current_count_sectors << 9) || - (raw_cmd->kernel_data != bio_data(current_req->bio) && - CT(COMMAND) == FD_WRITE && - (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max || - aligned_sector_t < buffer_min)) || - raw_cmd->length % (128 << SIZECODE) || - raw_cmd->length <= 0 || current_count_sectors <= 0) { - DPRINT("fractionary current count b=%lx s=%lx\n", - raw_cmd->length, current_count_sectors); - if (raw_cmd->kernel_data != bio_data(current_req->bio)) - pr_info("addr=%d, length=%ld\n", - (int)((raw_cmd->kernel_data - - floppy_track_buffer) >> 9), - current_count_sectors); - pr_info("st=%d ast=%d mse=%d msi=%d\n", - fsector_t, aligned_sector_t, max_sector, max_size); - pr_info("ssize=%x SIZECODE=%d\n", ssize, SIZECODE); - pr_info("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n", - COMMAND, SECTOR, HEAD, TRACK); - pr_info("buffer drive=%d\n", buffer_drive); - pr_info("buffer track=%d\n", buffer_track); - pr_info("buffer_min=%d\n", buffer_min); - pr_info("buffer_max=%d\n", buffer_max); - return 0; - } - - if (raw_cmd->kernel_data != bio_data(current_req->bio)) { - if (raw_cmd->kernel_data < floppy_track_buffer || - current_count_sectors < 0 || - raw_cmd->length < 0 || - raw_cmd->kernel_data + raw_cmd->length > - floppy_track_buffer + (max_buffer_sectors << 10)) { - DPRINT("buffer overrun in schedule dma\n"); - pr_info("fsector_t=%d buffer_min=%d current_count=%ld\n", - fsector_t, buffer_min, raw_cmd->length >> 9); - pr_info("current_count_sectors=%ld\n", - current_count_sectors); - if (CT(COMMAND) == FD_READ) - pr_info("read\n"); - if (CT(COMMAND) == FD_WRITE) - pr_info("write\n"); - return 0; - } - } else if (raw_cmd->length > blk_rq_bytes(current_req) || - current_count_sectors > blk_rq_sectors(current_req)) { - DPRINT("buffer overrun in direct transfer\n"); - return 0; - } else if (raw_cmd->length < current_count_sectors << 9) { - DPRINT("more sectors than bytes\n"); - pr_info("bytes=%ld\n", raw_cmd->length >> 9); - pr_info("sectors=%ld\n", current_count_sectors); - } - if (raw_cmd->length == 0) { - DPRINT("zero dma transfer attempted from make_raw_request\n"); - return 0; - } - - virtualdmabug_workaround(); - return 2; -} - -static int set_next_request(void) -{ - current_req = list_first_entry_or_null(&floppy_reqs, struct request, - queuelist); - if (current_req) { - current_req->error_count = 0; - list_del_init(¤t_req->queuelist); - } - return current_req != NULL; -} - -static void redo_fd_request(void) -{ - int drive; - int tmp; - - lastredo = jiffies; - if (current_drive < N_DRIVE) - floppy_off(current_drive); - -do_request: - if (!current_req) { - int pending; - - spin_lock_irq(&floppy_lock); - pending = set_next_request(); - spin_unlock_irq(&floppy_lock); - if (!pending) { - do_floppy = NULL; - unlock_fdc(); - return; - } - } - drive = (long)current_req->rq_disk->private_data; - set_fdc(drive); - reschedule_timeout(current_reqD, "redo fd request"); - - set_floppy(drive); - raw_cmd = &default_raw_cmd; - raw_cmd->flags = 0; - if (start_motor(redo_fd_request)) - return; - - disk_change(current_drive); - if (test_bit(current_drive, &fake_change) || - test_bit(FD_DISK_CHANGED_BIT, &DRS->flags)) { - DPRINT("disk absent or changed during operation\n"); - request_done(0); - goto do_request; - } - if (!_floppy) { /* Autodetection */ - if (!probing) { - DRS->probed_format = 0; - if (next_valid_format()) { - DPRINT("no autodetectable formats\n"); - _floppy = NULL; - request_done(0); - goto do_request; - } - } - probing = 1; - _floppy = floppy_type + DP->autodetect[DRS->probed_format]; - } else - probing = 0; - errors = &(current_req->error_count); - tmp = make_raw_rw_request(); - if (tmp < 2) { - request_done(tmp); - goto do_request; - } - - if (test_bit(FD_NEED_TWADDLE_BIT, &DRS->flags)) - twaddle(); - schedule_bh(floppy_start); - debugt(__func__, "queue fd request"); - return; -} - -static const struct cont_t rw_cont = { - .interrupt = rw_interrupt, - .redo = redo_fd_request, - .error = bad_flp_intr, - .done = request_done -}; - -static void process_fd_request(void) -{ - cont = &rw_cont; - schedule_bh(redo_fd_request); -} - -static blk_status_t floppy_queue_rq(struct blk_mq_hw_ctx *hctx, - const struct blk_mq_queue_data *bd) -{ - blk_mq_start_request(bd->rq); - - if (WARN(max_buffer_sectors == 0, - "VFS: %s called on non-open device\n", __func__)) - return BLK_STS_IOERR; - - if (WARN(atomic_read(&usage_count) == 0, - "warning: usage count=0, current_req=%p sect=%ld flags=%llx\n", - current_req, (long)blk_rq_pos(current_req), - (unsigned long long) current_req->cmd_flags)) - return BLK_STS_IOERR; - - spin_lock_irq(&floppy_lock); - list_add_tail(&bd->rq->queuelist, &floppy_reqs); - spin_unlock_irq(&floppy_lock); - - if (test_and_set_bit(0, &fdc_busy)) { - /* fdc busy, this new request will be treated when the - current one is done */ - is_alive(__func__, "old request running"); - return BLK_STS_OK; - } - - command_status = FD_COMMAND_NONE; - __reschedule_timeout(MAXTIMEOUT, "fd_request"); - set_fdc(0); - process_fd_request(); - is_alive(__func__, ""); - return BLK_STS_OK; -} - -static const struct cont_t poll_cont = { - .interrupt = success_and_wakeup, - .redo = floppy_ready, - .error = generic_failure, - .done = generic_done -}; - -static int poll_drive(bool interruptible, int flag) -{ - /* no auto-sense, just clear dcl */ - raw_cmd = &default_raw_cmd; - raw_cmd->flags = flag; - raw_cmd->track = 0; - raw_cmd->cmd_count = 0; - cont = &poll_cont; - debug_dcl(DP->flags, "setting NEWCHANGE in poll_drive\n"); - set_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags); - - return wait_til_done(floppy_ready, interruptible); -} - -/* - * User triggered reset - * ==================== - */ - -static void reset_intr(void) -{ - pr_info("weird, reset interrupt called\n"); -} - -static const struct cont_t reset_cont = { - .interrupt = reset_intr, - .redo = success_and_wakeup, - .error = generic_failure, - .done = generic_done -}; - -static int user_reset_fdc(int drive, int arg, bool interruptible) -{ - int ret; - - if (lock_fdc(drive)) - return -EINTR; - - if (arg == FD_RESET_ALWAYS) - FDCS->reset = 1; - if (FDCS->reset) { - cont = &reset_cont; - ret = wait_til_done(reset_fdc, interruptible); - if (ret == -EINTR) - return -EINTR; - } - process_fd_request(); - return 0; -} - -/* - * Misc Ioctl's and support - * ======================== - */ -static inline int fd_copyout(void __user *param, const void *address, - unsigned long size) -{ - return copy_to_user(param, address, size) ? -EFAULT : 0; -} - -static inline int fd_copyin(void __user *param, void *address, - unsigned long size) -{ - return copy_from_user(address, param, size) ? -EFAULT : 0; -} - -static const char *drive_name(int type, int drive) -{ - struct floppy_struct *floppy; - - if (type) - floppy = floppy_type + type; - else { - if (UDP->native_format) - floppy = floppy_type + UDP->native_format; - else - return "(null)"; - } - if (floppy->name) - return floppy->name; - else - return "(null)"; -} - -/* raw commands */ -static void raw_cmd_done(int flag) -{ - int i; - - if (!flag) { - raw_cmd->flags |= FD_RAW_FAILURE; - raw_cmd->flags |= FD_RAW_HARDFAILURE; - } else { - raw_cmd->reply_count = inr; - if (raw_cmd->reply_count > MAX_REPLIES) - raw_cmd->reply_count = 0; - for (i = 0; i < raw_cmd->reply_count; i++) - raw_cmd->reply[i] = reply_buffer[i]; - - if (raw_cmd->flags & (FD_RAW_READ | FD_RAW_WRITE)) { - unsigned long flags; - flags = claim_dma_lock(); - raw_cmd->length = fd_get_dma_residue(); - release_dma_lock(flags); - } - - if ((raw_cmd->flags & FD_RAW_SOFTFAILURE) && - (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0))) - raw_cmd->flags |= FD_RAW_FAILURE; - - if (disk_change(current_drive)) - raw_cmd->flags |= FD_RAW_DISK_CHANGE; - else - raw_cmd->flags &= ~FD_RAW_DISK_CHANGE; - if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER) - motor_off_callback(&motor_off_timer[current_drive]); - - if (raw_cmd->next && - (!(raw_cmd->flags & FD_RAW_FAILURE) || - !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) && - ((raw_cmd->flags & FD_RAW_FAILURE) || - !(raw_cmd->flags & FD_RAW_STOP_IF_SUCCESS))) { - raw_cmd = raw_cmd->next; - return; - } - } - generic_done(flag); -} - -static const struct cont_t raw_cmd_cont = { - .interrupt = success_and_wakeup, - .redo = floppy_start, - .error = generic_failure, - .done = raw_cmd_done -}; - -static int raw_cmd_copyout(int cmd, void __user *param, - struct floppy_raw_cmd *ptr) -{ - int ret; - - while (ptr) { - struct floppy_raw_cmd cmd = *ptr; - cmd.next = NULL; - cmd.kernel_data = NULL; - ret = copy_to_user(param, &cmd, sizeof(cmd)); - if (ret) - return -EFAULT; - param += sizeof(struct floppy_raw_cmd); - if ((ptr->flags & FD_RAW_READ) && ptr->buffer_length) { - if (ptr->length >= 0 && - ptr->length <= ptr->buffer_length) { - long length = ptr->buffer_length - ptr->length; - ret = fd_copyout(ptr->data, ptr->kernel_data, - length); - if (ret) - return ret; - } - } - ptr = ptr->next; - } - - return 0; -} - -static void raw_cmd_free(struct floppy_raw_cmd **ptr) -{ - struct floppy_raw_cmd *next; - struct floppy_raw_cmd *this; - - this = *ptr; - *ptr = NULL; - while (this) { - if (this->buffer_length) { - fd_dma_mem_free((unsigned long)this->kernel_data, - this->buffer_length); - this->buffer_length = 0; - } - next = this->next; - kfree(this); - this = next; - } -} - -static int raw_cmd_copyin(int cmd, void __user *param, - struct floppy_raw_cmd **rcmd) -{ - struct floppy_raw_cmd *ptr; - int ret; - int i; - - *rcmd = NULL; - -loop: - ptr = kmalloc(sizeof(struct floppy_raw_cmd), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - *rcmd = ptr; - ret = copy_from_user(ptr, param, sizeof(*ptr)); - ptr->next = NULL; - ptr->buffer_length = 0; - ptr->kernel_data = NULL; - if (ret) - return -EFAULT; - param += sizeof(struct floppy_raw_cmd); - if (ptr->cmd_count > 33) - /* the command may now also take up the space - * initially intended for the reply & the - * reply count. Needed for long 82078 commands - * such as RESTORE, which takes ... 17 command - * bytes. Murphy's law #137: When you reserve - * 16 bytes for a structure, you'll one day - * discover that you really need 17... - */ - return -EINVAL; - - for (i = 0; i < 16; i++) - ptr->reply[i] = 0; - ptr->resultcode = 0; - - if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) { - if (ptr->length <= 0) - return -EINVAL; - ptr->kernel_data = (char *)fd_dma_mem_alloc(ptr->length); - fallback_on_nodma_alloc(&ptr->kernel_data, ptr->length); - if (!ptr->kernel_data) - return -ENOMEM; - ptr->buffer_length = ptr->length; - } - if (ptr->flags & FD_RAW_WRITE) { - ret = fd_copyin(ptr->data, ptr->kernel_data, ptr->length); - if (ret) - return ret; - } - - if (ptr->flags & FD_RAW_MORE) { - rcmd = &(ptr->next); - ptr->rate &= 0x43; - goto loop; - } - - return 0; -} - -static int raw_cmd_ioctl(int cmd, void __user *param) -{ - struct floppy_raw_cmd *my_raw_cmd; - int drive; - int ret2; - int ret; - - if (FDCS->rawcmd <= 1) - FDCS->rawcmd = 1; - for (drive = 0; drive < N_DRIVE; drive++) { - if (FDC(drive) != fdc) - continue; - if (drive == current_drive) { - if (UDRS->fd_ref > 1) { - FDCS->rawcmd = 2; - break; - } - } else if (UDRS->fd_ref) { - FDCS->rawcmd = 2; - break; - } - } - - if (FDCS->reset) - return -EIO; - - ret = raw_cmd_copyin(cmd, param, &my_raw_cmd); - if (ret) { - raw_cmd_free(&my_raw_cmd); - return ret; - } - - raw_cmd = my_raw_cmd; - cont = &raw_cmd_cont; - ret = wait_til_done(floppy_start, true); - debug_dcl(DP->flags, "calling disk change from raw_cmd ioctl\n"); - - if (ret != -EINTR && FDCS->reset) - ret = -EIO; - - DRS->track = NO_TRACK; - - ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd); - if (!ret) - ret = ret2; - raw_cmd_free(&my_raw_cmd); - return ret; -} - -static int invalidate_drive(struct block_device *bdev) -{ - /* invalidate the buffer track to force a reread */ - set_bit((long)bdev->bd_disk->private_data, &fake_change); - process_fd_request(); - check_disk_change(bdev); - return 0; -} - -static int set_geometry(unsigned int cmd, struct floppy_struct *g, - int drive, int type, struct block_device *bdev) -{ - int cnt; - - /* sanity checking for parameters. */ - if (g->sect <= 0 || - g->head <= 0 || - g->track <= 0 || g->track > UDP->tracks >> STRETCH(g) || - /* check if reserved bits are set */ - (g->stretch & ~(FD_STRETCH | FD_SWAPSIDES | FD_SECTBASEMASK)) != 0) - return -EINVAL; - if (type) { - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - mutex_lock(&open_lock); - if (lock_fdc(drive)) { - mutex_unlock(&open_lock); - return -EINTR; - } - floppy_type[type] = *g; - floppy_type[type].name = "user format"; - for (cnt = type << 2; cnt < (type << 2) + 4; cnt++) - floppy_sizes[cnt] = floppy_sizes[cnt + 0x80] = - floppy_type[type].size + 1; - process_fd_request(); - for (cnt = 0; cnt < N_DRIVE; cnt++) { - struct block_device *bdev = opened_bdev[cnt]; - if (!bdev || ITYPE(drive_state[cnt].fd_device) != type) - continue; - __invalidate_device(bdev, true); - } - mutex_unlock(&open_lock); - } else { - int oldStretch; - - if (lock_fdc(drive)) - return -EINTR; - if (cmd != FDDEFPRM) { - /* notice a disk change immediately, else - * we lose our settings immediately*/ - if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) - return -EINTR; - } - oldStretch = g->stretch; - user_params[drive] = *g; - if (buffer_drive == drive) - SUPBOUND(buffer_max, user_params[drive].sect); - current_type[drive] = &user_params[drive]; - floppy_sizes[drive] = user_params[drive].size; - if (cmd == FDDEFPRM) - DRS->keep_data = -1; - else - DRS->keep_data = 1; - /* invalidation. Invalidate only when needed, i.e. - * when there are already sectors in the buffer cache - * whose number will change. This is useful, because - * mtools often changes the geometry of the disk after - * looking at the boot block */ - if (DRS->maxblock > user_params[drive].sect || - DRS->maxtrack || - ((user_params[drive].sect ^ oldStretch) & - (FD_SWAPSIDES | FD_SECTBASEMASK))) - invalidate_drive(bdev); - else - process_fd_request(); - } - return 0; -} - -/* handle obsolete ioctl's */ -static unsigned int ioctl_table[] = { - FDCLRPRM, - FDSETPRM, - FDDEFPRM, - FDGETPRM, - FDMSGON, - FDMSGOFF, - FDFMTBEG, - FDFMTTRK, - FDFMTEND, - FDSETEMSGTRESH, - FDFLUSH, - FDSETMAXERRS, - FDGETMAXERRS, - FDGETDRVTYP, - FDSETDRVPRM, - FDGETDRVPRM, - FDGETDRVSTAT, - FDPOLLDRVSTAT, - FDRESET, - FDGETFDCSTAT, - FDWERRORCLR, - FDWERRORGET, - FDRAWCMD, - FDEJECT, - FDTWADDLE -}; - -static int normalize_ioctl(unsigned int *cmd, int *size) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ioctl_table); i++) { - if ((*cmd & 0xffff) == (ioctl_table[i] & 0xffff)) { - *size = _IOC_SIZE(*cmd); - *cmd = ioctl_table[i]; - if (*size > _IOC_SIZE(*cmd)) { - pr_info("ioctl not yet supported\n"); - return -EFAULT; - } - return 0; - } - } - return -EINVAL; -} - -static int get_floppy_geometry(int drive, int type, struct floppy_struct **g) -{ - if (type) - *g = &floppy_type[type]; - else { - if (lock_fdc(drive)) - return -EINTR; - if (poll_drive(false, 0) == -EINTR) - return -EINTR; - process_fd_request(); - *g = current_type[drive]; - } - if (!*g) - return -ENODEV; - return 0; -} - -static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo) -{ - int drive = (long)bdev->bd_disk->private_data; - int type = ITYPE(drive_state[drive].fd_device); - struct floppy_struct *g; - int ret; - - ret = get_floppy_geometry(drive, type, &g); - if (ret) - return ret; - - geo->heads = g->head; - geo->sectors = g->sect; - geo->cylinders = g->track; - return 0; -} - -static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, - unsigned long param) -{ - int drive = (long)bdev->bd_disk->private_data; - int type = ITYPE(UDRS->fd_device); - int i; - int ret; - int size; - union inparam { - struct floppy_struct g; /* geometry */ - struct format_descr f; - struct floppy_max_errors max_errors; - struct floppy_drive_params dp; - } inparam; /* parameters coming from user space */ - const void *outparam; /* parameters passed back to user space */ - - /* convert compatibility eject ioctls into floppy eject ioctl. - * We do this in order to provide a means to eject floppy disks before - * installing the new fdutils package */ - if (cmd == CDROMEJECT || /* CD-ROM eject */ - cmd == 0x6470) { /* SunOS floppy eject */ - DPRINT("obsolete eject ioctl\n"); - DPRINT("please use floppycontrol --eject\n"); - cmd = FDEJECT; - } - - if (!((cmd & 0xff00) == 0x0200)) - return -EINVAL; - - /* convert the old style command into a new style command */ - ret = normalize_ioctl(&cmd, &size); - if (ret) - return ret; - - /* permission checks */ - if (((cmd & 0x40) && !(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL))) || - ((cmd & 0x80) && !capable(CAP_SYS_ADMIN))) - return -EPERM; - - if (WARN_ON(size < 0 || size > sizeof(inparam))) - return -EINVAL; - - /* copyin */ - memset(&inparam, 0, sizeof(inparam)); - if (_IOC_DIR(cmd) & _IOC_WRITE) { - ret = fd_copyin((void __user *)param, &inparam, size); - if (ret) - return ret; - } - - switch (cmd) { - case FDEJECT: - if (UDRS->fd_ref != 1) - /* somebody else has this drive open */ - return -EBUSY; - if (lock_fdc(drive)) - return -EINTR; - - /* do the actual eject. Fails on - * non-Sparc architectures */ - ret = fd_eject(UNIT(drive)); - - set_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); - set_bit(FD_VERIFY_BIT, &UDRS->flags); - process_fd_request(); - return ret; - case FDCLRPRM: - if (lock_fdc(drive)) - return -EINTR; - current_type[drive] = NULL; - floppy_sizes[drive] = MAX_DISK_SIZE << 1; - UDRS->keep_data = 0; - return invalidate_drive(bdev); - case FDSETPRM: - case FDDEFPRM: - return set_geometry(cmd, &inparam.g, drive, type, bdev); - case FDGETPRM: - ret = get_floppy_geometry(drive, type, - (struct floppy_struct **)&outparam); - if (ret) - return ret; - memcpy(&inparam.g, outparam, - offsetof(struct floppy_struct, name)); - outparam = &inparam.g; - break; - case FDMSGON: - UDP->flags |= FTD_MSG; - return 0; - case FDMSGOFF: - UDP->flags &= ~FTD_MSG; - return 0; - case FDFMTBEG: - if (lock_fdc(drive)) - return -EINTR; - if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) - return -EINTR; - ret = UDRS->flags; - process_fd_request(); - if (ret & FD_VERIFY) - return -ENODEV; - if (!(ret & FD_DISK_WRITABLE)) - return -EROFS; - return 0; - case FDFMTTRK: - if (UDRS->fd_ref != 1) - return -EBUSY; - return do_format(drive, &inparam.f); - case FDFMTEND: - case FDFLUSH: - if (lock_fdc(drive)) - return -EINTR; - return invalidate_drive(bdev); - case FDSETEMSGTRESH: - UDP->max_errors.reporting = (unsigned short)(param & 0x0f); - return 0; - case FDGETMAXERRS: - outparam = &UDP->max_errors; - break; - case FDSETMAXERRS: - UDP->max_errors = inparam.max_errors; - break; - case FDGETDRVTYP: - outparam = drive_name(type, drive); - SUPBOUND(size, strlen((const char *)outparam) + 1); - break; - case FDSETDRVPRM: - *UDP = inparam.dp; - break; - case FDGETDRVPRM: - outparam = UDP; - break; - case FDPOLLDRVSTAT: - if (lock_fdc(drive)) - return -EINTR; - if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) - return -EINTR; - process_fd_request(); - /* fall through */ - case FDGETDRVSTAT: - outparam = UDRS; - break; - case FDRESET: - return user_reset_fdc(drive, (int)param, true); - case FDGETFDCSTAT: - outparam = UFDCS; - break; - case FDWERRORCLR: - memset(UDRWE, 0, sizeof(*UDRWE)); - return 0; - case FDWERRORGET: - outparam = UDRWE; - break; - case FDRAWCMD: - if (type) - return -EINVAL; - if (lock_fdc(drive)) - return -EINTR; - set_floppy(drive); - i = raw_cmd_ioctl(cmd, (void __user *)param); - if (i == -EINTR) - return -EINTR; - process_fd_request(); - return i; - case FDTWADDLE: - if (lock_fdc(drive)) - return -EINTR; - twaddle(); - process_fd_request(); - return 0; - default: - return -EINVAL; - } - - if (_IOC_DIR(cmd) & _IOC_READ) - return fd_copyout((void __user *)param, outparam, size); - - return 0; -} - -static int fd_ioctl(struct block_device *bdev, fmode_t mode, - unsigned int cmd, unsigned long param) -{ - int ret; - - mutex_lock(&floppy_mutex); - ret = fd_locked_ioctl(bdev, mode, cmd, param); - mutex_unlock(&floppy_mutex); - - return ret; -} - -#ifdef CONFIG_COMPAT - -struct compat_floppy_drive_params { - char cmos; - compat_ulong_t max_dtr; - compat_ulong_t hlt; - compat_ulong_t hut; - compat_ulong_t srt; - compat_ulong_t spinup; - compat_ulong_t spindown; - unsigned char spindown_offset; - unsigned char select_delay; - unsigned char rps; - unsigned char tracks; - compat_ulong_t timeout; - unsigned char interleave_sect; - struct floppy_max_errors max_errors; - char flags; - char read_track; - short autodetect[8]; - compat_int_t checkfreq; - compat_int_t native_format; -}; - -struct compat_floppy_drive_struct { - signed char flags; - compat_ulong_t spinup_date; - compat_ulong_t select_date; - compat_ulong_t first_read_date; - short probed_format; - short track; - short maxblock; - short maxtrack; - compat_int_t generation; - compat_int_t keep_data; - compat_int_t fd_ref; - compat_int_t fd_device; - compat_int_t last_checked; - compat_caddr_t dmabuf; - compat_int_t bufblocks; -}; - -struct compat_floppy_fdc_state { - compat_int_t spec1; - compat_int_t spec2; - compat_int_t dtr; - unsigned char version; - unsigned char dor; - compat_ulong_t address; - unsigned int rawcmd : 2; - unsigned int reset : 1; - unsigned int need_configure : 1; - unsigned int perp_mode : 2; - unsigned int has_fifo : 1; - unsigned int driver_version; - unsigned char track[4]; -}; - -struct compat_floppy_write_errors { - unsigned int write_errors; - compat_ulong_t first_error_sector; - compat_int_t first_error_generation; - compat_ulong_t last_error_sector; - compat_int_t last_error_generation; - compat_uint_t badness; -}; - -#define FDSETPRM32 _IOW(2, 0x42, struct compat_floppy_struct) -#define FDDEFPRM32 _IOW(2, 0x43, struct compat_floppy_struct) -#define FDSETDRVPRM32 _IOW(2, 0x90, struct compat_floppy_drive_params) -#define FDGETDRVPRM32 _IOR(2, 0x11, struct compat_floppy_drive_params) -#define FDGETDRVSTAT32 _IOR(2, 0x12, struct compat_floppy_drive_struct) -#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct compat_floppy_drive_struct) -#define FDGETFDCSTAT32 _IOR(2, 0x15, struct compat_floppy_fdc_state) -#define FDWERRORGET32 _IOR(2, 0x17, struct compat_floppy_write_errors) - -static int compat_set_geometry(struct block_device *bdev, fmode_t mode, unsigned int cmd, - struct compat_floppy_struct __user *arg) -{ - struct floppy_struct v; - int drive, type; - int err; - - BUILD_BUG_ON(offsetof(struct floppy_struct, name) != - offsetof(struct compat_floppy_struct, name)); - - if (!(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL))) - return -EPERM; - - memset(&v, 0, sizeof(struct floppy_struct)); - if (copy_from_user(&v, arg, offsetof(struct floppy_struct, name))) - return -EFAULT; - - mutex_lock(&floppy_mutex); - drive = (long)bdev->bd_disk->private_data; - type = ITYPE(UDRS->fd_device); - err = set_geometry(cmd == FDSETPRM32 ? FDSETPRM : FDDEFPRM, - &v, drive, type, bdev); - mutex_unlock(&floppy_mutex); - return err; -} - -static int compat_get_prm(int drive, - struct compat_floppy_struct __user *arg) -{ - struct compat_floppy_struct v; - struct floppy_struct *p; - int err; - - memset(&v, 0, sizeof(v)); - mutex_lock(&floppy_mutex); - err = get_floppy_geometry(drive, ITYPE(UDRS->fd_device), &p); - if (err) { - mutex_unlock(&floppy_mutex); - return err; - } - memcpy(&v, p, offsetof(struct floppy_struct, name)); - mutex_unlock(&floppy_mutex); - if (copy_to_user(arg, &v, sizeof(struct compat_floppy_struct))) - return -EFAULT; - return 0; -} - -static int compat_setdrvprm(int drive, - struct compat_floppy_drive_params __user *arg) -{ - struct compat_floppy_drive_params v; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (copy_from_user(&v, arg, sizeof(struct compat_floppy_drive_params))) - return -EFAULT; - mutex_lock(&floppy_mutex); - UDP->cmos = v.cmos; - UDP->max_dtr = v.max_dtr; - UDP->hlt = v.hlt; - UDP->hut = v.hut; - UDP->srt = v.srt; - UDP->spinup = v.spinup; - UDP->spindown = v.spindown; - UDP->spindown_offset = v.spindown_offset; - UDP->select_delay = v.select_delay; - UDP->rps = v.rps; - UDP->tracks = v.tracks; - UDP->timeout = v.timeout; - UDP->interleave_sect = v.interleave_sect; - UDP->max_errors = v.max_errors; - UDP->flags = v.flags; - UDP->read_track = v.read_track; - memcpy(UDP->autodetect, v.autodetect, sizeof(v.autodetect)); - UDP->checkfreq = v.checkfreq; - UDP->native_format = v.native_format; - mutex_unlock(&floppy_mutex); - return 0; -} - -static int compat_getdrvprm(int drive, - struct compat_floppy_drive_params __user *arg) -{ - struct compat_floppy_drive_params v; - - memset(&v, 0, sizeof(struct compat_floppy_drive_params)); - mutex_lock(&floppy_mutex); - v.cmos = UDP->cmos; - v.max_dtr = UDP->max_dtr; - v.hlt = UDP->hlt; - v.hut = UDP->hut; - v.srt = UDP->srt; - v.spinup = UDP->spinup; - v.spindown = UDP->spindown; - v.spindown_offset = UDP->spindown_offset; - v.select_delay = UDP->select_delay; - v.rps = UDP->rps; - v.tracks = UDP->tracks; - v.timeout = UDP->timeout; - v.interleave_sect = UDP->interleave_sect; - v.max_errors = UDP->max_errors; - v.flags = UDP->flags; - v.read_track = UDP->read_track; - memcpy(v.autodetect, UDP->autodetect, sizeof(v.autodetect)); - v.checkfreq = UDP->checkfreq; - v.native_format = UDP->native_format; - mutex_unlock(&floppy_mutex); - - if (copy_from_user(arg, &v, sizeof(struct compat_floppy_drive_params))) - return -EFAULT; - return 0; -} - -static int compat_getdrvstat(int drive, bool poll, - struct compat_floppy_drive_struct __user *arg) -{ - struct compat_floppy_drive_struct v; - - memset(&v, 0, sizeof(struct compat_floppy_drive_struct)); - mutex_lock(&floppy_mutex); - - if (poll) { - if (lock_fdc(drive)) - goto Eintr; - if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) - goto Eintr; - process_fd_request(); - } - v.spinup_date = UDRS->spinup_date; - v.select_date = UDRS->select_date; - v.first_read_date = UDRS->first_read_date; - v.probed_format = UDRS->probed_format; - v.track = UDRS->track; - v.maxblock = UDRS->maxblock; - v.maxtrack = UDRS->maxtrack; - v.generation = UDRS->generation; - v.keep_data = UDRS->keep_data; - v.fd_ref = UDRS->fd_ref; - v.fd_device = UDRS->fd_device; - v.last_checked = UDRS->last_checked; - v.dmabuf = (uintptr_t)UDRS->dmabuf; - v.bufblocks = UDRS->bufblocks; - mutex_unlock(&floppy_mutex); - - if (copy_from_user(arg, &v, sizeof(struct compat_floppy_drive_struct))) - return -EFAULT; - return 0; -Eintr: - mutex_unlock(&floppy_mutex); - return -EINTR; -} - -static int compat_getfdcstat(int drive, - struct compat_floppy_fdc_state __user *arg) -{ - struct compat_floppy_fdc_state v32; - struct floppy_fdc_state v; - - mutex_lock(&floppy_mutex); - v = *UFDCS; - mutex_unlock(&floppy_mutex); - - memset(&v32, 0, sizeof(struct compat_floppy_fdc_state)); - v32.spec1 = v.spec1; - v32.spec2 = v.spec2; - v32.dtr = v.dtr; - v32.version = v.version; - v32.dor = v.dor; - v32.address = v.address; - v32.rawcmd = v.rawcmd; - v32.reset = v.reset; - v32.need_configure = v.need_configure; - v32.perp_mode = v.perp_mode; - v32.has_fifo = v.has_fifo; - v32.driver_version = v.driver_version; - memcpy(v32.track, v.track, 4); - if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_fdc_state))) - return -EFAULT; - return 0; -} - -static int compat_werrorget(int drive, - struct compat_floppy_write_errors __user *arg) -{ - struct compat_floppy_write_errors v32; - struct floppy_write_errors v; - - memset(&v32, 0, sizeof(struct compat_floppy_write_errors)); - mutex_lock(&floppy_mutex); - v = *UDRWE; - mutex_unlock(&floppy_mutex); - v32.write_errors = v.write_errors; - v32.first_error_sector = v.first_error_sector; - v32.first_error_generation = v.first_error_generation; - v32.last_error_sector = v.last_error_sector; - v32.last_error_generation = v.last_error_generation; - v32.badness = v.badness; - if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_write_errors))) - return -EFAULT; - return 0; -} - -static int fd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, - unsigned long param) -{ - int drive = (long)bdev->bd_disk->private_data; - switch (cmd) { - case FDMSGON: - case FDMSGOFF: - case FDSETEMSGTRESH: - case FDFLUSH: - case FDWERRORCLR: - case FDEJECT: - case FDCLRPRM: - case FDFMTBEG: - case FDRESET: - case FDTWADDLE: - return fd_ioctl(bdev, mode, cmd, param); - case FDSETMAXERRS: - case FDGETMAXERRS: - case FDGETDRVTYP: - case FDFMTEND: - case FDFMTTRK: - case FDRAWCMD: - return fd_ioctl(bdev, mode, cmd, - (unsigned long)compat_ptr(param)); - case FDSETPRM32: - case FDDEFPRM32: - return compat_set_geometry(bdev, mode, cmd, compat_ptr(param)); - case FDGETPRM32: - return compat_get_prm(drive, compat_ptr(param)); - case FDSETDRVPRM32: - return compat_setdrvprm(drive, compat_ptr(param)); - case FDGETDRVPRM32: - return compat_getdrvprm(drive, compat_ptr(param)); - case FDPOLLDRVSTAT32: - return compat_getdrvstat(drive, true, compat_ptr(param)); - case FDGETDRVSTAT32: - return compat_getdrvstat(drive, false, compat_ptr(param)); - case FDGETFDCSTAT32: - return compat_getfdcstat(drive, compat_ptr(param)); - case FDWERRORGET32: - return compat_werrorget(drive, compat_ptr(param)); - } - return -EINVAL; -} -#endif - -static void __init config_types(void) -{ - bool has_drive = false; - int drive; - - /* read drive info out of physical CMOS */ - drive = 0; - if (!UDP->cmos) - UDP->cmos = FLOPPY0_TYPE; - drive = 1; - if (!UDP->cmos && FLOPPY1_TYPE) - UDP->cmos = FLOPPY1_TYPE; - - /* FIXME: additional physical CMOS drive detection should go here */ - - for (drive = 0; drive < N_DRIVE; drive++) { - unsigned int type = UDP->cmos; - struct floppy_drive_params *params; - const char *name = NULL; - char temparea[32]; - - if (type < ARRAY_SIZE(default_drive_params)) { - params = &default_drive_params[type].params; - if (type) { - name = default_drive_params[type].name; - allowed_drive_mask |= 1 << drive; - } else - allowed_drive_mask &= ~(1 << drive); - } else { - params = &default_drive_params[0].params; - snprintf(temparea, sizeof(temparea), - "unknown type %d (usb?)", type); - name = temparea; - } - if (name) { - const char *prepend; - if (!has_drive) { - prepend = ""; - has_drive = true; - pr_info("Floppy drive(s):"); - } else { - prepend = ","; - } - - pr_cont("%s fd%d is %s", prepend, drive, name); - } - *UDP = *params; - } - - if (has_drive) - pr_cont("\n"); -} - -static void floppy_release(struct gendisk *disk, fmode_t mode) -{ - int drive = (long)disk->private_data; - - mutex_lock(&floppy_mutex); - mutex_lock(&open_lock); - if (!UDRS->fd_ref--) { - DPRINT("floppy_release with fd_ref == 0"); - UDRS->fd_ref = 0; - } - if (!UDRS->fd_ref) - opened_bdev[drive] = NULL; - mutex_unlock(&open_lock); - mutex_unlock(&floppy_mutex); -} - -/* - * floppy_open check for aliasing (/dev/fd0 can be the same as - * /dev/PS0 etc), and disallows simultaneous access to the same - * drive with different device numbers. - */ -static int floppy_open(struct block_device *bdev, fmode_t mode) -{ - int drive = (long)bdev->bd_disk->private_data; - int old_dev, new_dev; - int try; - int res = -EBUSY; - char *tmp; - - mutex_lock(&floppy_mutex); - mutex_lock(&open_lock); - old_dev = UDRS->fd_device; - if (opened_bdev[drive] && opened_bdev[drive] != bdev) - goto out2; - - if (!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)) { - set_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); - set_bit(FD_VERIFY_BIT, &UDRS->flags); - } - - UDRS->fd_ref++; - - opened_bdev[drive] = bdev; - - res = -ENXIO; - - if (!floppy_track_buffer) { - /* if opening an ED drive, reserve a big buffer, - * else reserve a small one */ - if ((UDP->cmos == 6) || (UDP->cmos == 5)) - try = 64; /* Only 48 actually useful */ - else - try = 32; /* Only 24 actually useful */ - - tmp = (char *)fd_dma_mem_alloc(1024 * try); - if (!tmp && !floppy_track_buffer) { - try >>= 1; /* buffer only one side */ - INFBOUND(try, 16); - tmp = (char *)fd_dma_mem_alloc(1024 * try); - } - if (!tmp && !floppy_track_buffer) - fallback_on_nodma_alloc(&tmp, 2048 * try); - if (!tmp && !floppy_track_buffer) { - DPRINT("Unable to allocate DMA memory\n"); - goto out; - } - if (floppy_track_buffer) { - if (tmp) - fd_dma_mem_free((unsigned long)tmp, try * 1024); - } else { - buffer_min = buffer_max = -1; - floppy_track_buffer = tmp; - max_buffer_sectors = try; - } - } - - new_dev = MINOR(bdev->bd_dev); - UDRS->fd_device = new_dev; - set_capacity(disks[drive], floppy_sizes[new_dev]); - if (old_dev != -1 && old_dev != new_dev) { - if (buffer_drive == drive) - buffer_track = -1; - } - - if (UFDCS->rawcmd == 1) - UFDCS->rawcmd = 2; - - if (!(mode & FMODE_NDELAY)) { - if (mode & (FMODE_READ|FMODE_WRITE)) { - UDRS->last_checked = 0; - clear_bit(FD_OPEN_SHOULD_FAIL_BIT, &UDRS->flags); - check_disk_change(bdev); - if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags)) - goto out; - if (test_bit(FD_OPEN_SHOULD_FAIL_BIT, &UDRS->flags)) - goto out; - } - res = -EROFS; - if ((mode & FMODE_WRITE) && - !test_bit(FD_DISK_WRITABLE_BIT, &UDRS->flags)) - goto out; - } - mutex_unlock(&open_lock); - mutex_unlock(&floppy_mutex); - return 0; -out: - UDRS->fd_ref--; - - if (!UDRS->fd_ref) - opened_bdev[drive] = NULL; -out2: - mutex_unlock(&open_lock); - mutex_unlock(&floppy_mutex); - return res; -} - -/* - * Check if the disk has been changed or if a change has been faked. - */ -static unsigned int floppy_check_events(struct gendisk *disk, - unsigned int clearing) -{ - int drive = (long)disk->private_data; - - if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) || - test_bit(FD_VERIFY_BIT, &UDRS->flags)) - return DISK_EVENT_MEDIA_CHANGE; - - if (time_after(jiffies, UDRS->last_checked + UDP->checkfreq)) { - if (lock_fdc(drive)) - return 0; - poll_drive(false, 0); - process_fd_request(); - } - - if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) || - test_bit(FD_VERIFY_BIT, &UDRS->flags) || - test_bit(drive, &fake_change) || - drive_no_geom(drive)) - return DISK_EVENT_MEDIA_CHANGE; - return 0; -} - -/* - * This implements "read block 0" for floppy_revalidate(). - * Needed for format autodetection, checking whether there is - * a disk in the drive, and whether that disk is writable. - */ - -struct rb0_cbdata { - int drive; - struct completion complete; -}; - -static void floppy_rb0_cb(struct bio *bio) -{ - struct rb0_cbdata *cbdata = (struct rb0_cbdata *)bio->bi_private; - int drive = cbdata->drive; - - if (bio->bi_status) { - pr_info("floppy: error %d while reading block 0\n", - bio->bi_status); - set_bit(FD_OPEN_SHOULD_FAIL_BIT, &UDRS->flags); - } - complete(&cbdata->complete); -} - -static int __floppy_read_block_0(struct block_device *bdev, int drive) -{ - struct bio bio; - struct bio_vec bio_vec; - struct page *page; - struct rb0_cbdata cbdata; - size_t size; - - page = alloc_page(GFP_NOIO); - if (!page) { - process_fd_request(); - return -ENOMEM; - } - - size = bdev->bd_block_size; - if (!size) - size = 1024; - - cbdata.drive = drive; - - bio_init(&bio, &bio_vec, 1); - bio_set_dev(&bio, bdev); - bio_add_page(&bio, page, size, 0); - - bio.bi_iter.bi_sector = 0; - bio.bi_flags |= (1 << BIO_QUIET); - bio.bi_private = &cbdata; - bio.bi_end_io = floppy_rb0_cb; - bio_set_op_attrs(&bio, REQ_OP_READ, 0); - - init_completion(&cbdata.complete); - - submit_bio(&bio); - process_fd_request(); - - wait_for_completion(&cbdata.complete); - - __free_page(page); - - return 0; -} - -/* revalidate the floppy disk, i.e. trigger format autodetection by reading - * the bootblock (block 0). "Autodetection" is also needed to check whether - * there is a disk in the drive at all... Thus we also do it for fixed - * geometry formats */ -static int floppy_revalidate(struct gendisk *disk) -{ - int drive = (long)disk->private_data; - int cf; - int res = 0; - - if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) || - test_bit(FD_VERIFY_BIT, &UDRS->flags) || - test_bit(drive, &fake_change) || - drive_no_geom(drive)) { - if (WARN(atomic_read(&usage_count) == 0, - "VFS: revalidate called on non-open device.\n")) - return -EFAULT; - - res = lock_fdc(drive); - if (res) - return res; - cf = (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) || - test_bit(FD_VERIFY_BIT, &UDRS->flags)); - if (!(cf || test_bit(drive, &fake_change) || drive_no_geom(drive))) { - process_fd_request(); /*already done by another thread */ - return 0; - } - UDRS->maxblock = 0; - UDRS->maxtrack = 0; - if (buffer_drive == drive) - buffer_track = -1; - clear_bit(drive, &fake_change); - clear_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); - if (cf) - UDRS->generation++; - if (drive_no_geom(drive)) { - /* auto-sensing */ - res = __floppy_read_block_0(opened_bdev[drive], drive); - } else { - if (cf) - poll_drive(false, FD_RAW_NEED_DISK); - process_fd_request(); - } - } - set_capacity(disk, floppy_sizes[UDRS->fd_device]); - return res; -} - -static const struct block_device_operations floppy_fops = { - .owner = THIS_MODULE, - .open = floppy_open, - .release = floppy_release, - .ioctl = fd_ioctl, - .getgeo = fd_getgeo, - .check_events = floppy_check_events, - .revalidate_disk = floppy_revalidate, -#ifdef CONFIG_COMPAT - .compat_ioctl = fd_compat_ioctl, -#endif -}; - -/* - * Floppy Driver initialization - * ============================= - */ - -/* Determine the floppy disk controller type */ -/* This routine was written by David C. Niemi */ -static char __init get_fdc_version(void) -{ - int r; - - output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */ - if (FDCS->reset) - return FDC_NONE; - r = result(); - if (r <= 0x00) - return FDC_NONE; /* No FDC present ??? */ - if ((r == 1) && (reply_buffer[0] == 0x80)) { - pr_info("FDC %d is an 8272A\n", fdc); - return FDC_8272A; /* 8272a/765 don't know DUMPREGS */ - } - if (r != 10) { - pr_info("FDC %d init: DUMPREGS: unexpected return of %d bytes.\n", - fdc, r); - return FDC_UNKNOWN; - } - - if (!fdc_configure()) { - pr_info("FDC %d is an 82072\n", fdc); - return FDC_82072; /* 82072 doesn't know CONFIGURE */ - } - - output_byte(FD_PERPENDICULAR); - if (need_more_output() == MORE_OUTPUT) { - output_byte(0); - } else { - pr_info("FDC %d is an 82072A\n", fdc); - return FDC_82072A; /* 82072A as found on Sparcs. */ - } - - output_byte(FD_UNLOCK); - r = result(); - if ((r == 1) && (reply_buffer[0] == 0x80)) { - pr_info("FDC %d is a pre-1991 82077\n", fdc); - return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know - * LOCK/UNLOCK */ - } - if ((r != 1) || (reply_buffer[0] != 0x00)) { - pr_info("FDC %d init: UNLOCK: unexpected return of %d bytes.\n", - fdc, r); - return FDC_UNKNOWN; - } - output_byte(FD_PARTID); - r = result(); - if (r != 1) { - pr_info("FDC %d init: PARTID: unexpected return of %d bytes.\n", - fdc, r); - return FDC_UNKNOWN; - } - if (reply_buffer[0] == 0x80) { - pr_info("FDC %d is a post-1991 82077\n", fdc); - return FDC_82077; /* Revised 82077AA passes all the tests */ - } - switch (reply_buffer[0] >> 5) { - case 0x0: - /* Either a 82078-1 or a 82078SL running at 5Volt */ - pr_info("FDC %d is an 82078.\n", fdc); - return FDC_82078; - case 0x1: - pr_info("FDC %d is a 44pin 82078\n", fdc); - return FDC_82078; - case 0x2: - pr_info("FDC %d is a S82078B\n", fdc); - return FDC_S82078B; - case 0x3: - pr_info("FDC %d is a National Semiconductor PC87306\n", fdc); - return FDC_87306; - default: - pr_info("FDC %d init: 82078 variant with unknown PARTID=%d.\n", - fdc, reply_buffer[0] >> 5); - return FDC_82078_UNKN; - } -} /* get_fdc_version */ - -/* lilo configuration */ - -static void __init floppy_set_flags(int *ints, int param, int param2) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) { - if (param) - default_drive_params[i].params.flags |= param2; - else - default_drive_params[i].params.flags &= ~param2; - } - DPRINT("%s flag 0x%x\n", param2 ? "Setting" : "Clearing", param); -} - -static void __init daring(int *ints, int param, int param2) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) { - if (param) { - default_drive_params[i].params.select_delay = 0; - default_drive_params[i].params.flags |= - FD_SILENT_DCL_CLEAR; - } else { - default_drive_params[i].params.select_delay = - 2 * HZ / 100; - default_drive_params[i].params.flags &= - ~FD_SILENT_DCL_CLEAR; - } - } - DPRINT("Assuming %s floppy hardware\n", param ? "standard" : "broken"); -} - -static void __init set_cmos(int *ints, int dummy, int dummy2) -{ - int current_drive = 0; - - if (ints[0] != 2) { - DPRINT("wrong number of parameters for CMOS\n"); - return; - } - current_drive = ints[1]; - if (current_drive < 0 || current_drive >= 8) { - DPRINT("bad drive for set_cmos\n"); - return; - } -#if N_FDC > 1 - if (current_drive >= 4 && !FDC2) - FDC2 = 0x370; -#endif - DP->cmos = ints[2]; - DPRINT("setting CMOS code to %d\n", ints[2]); -} - -static struct param_table { - const char *name; - void (*fn) (int *ints, int param, int param2); - int *var; - int def_param; - int param2; -} config_params[] __initdata = { - {"allowed_drive_mask", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */ - {"all_drives", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */ - {"asus_pci", NULL, &allowed_drive_mask, 0x33, 0}, - {"irq", NULL, &FLOPPY_IRQ, 6, 0}, - {"dma", NULL, &FLOPPY_DMA, 2, 0}, - {"daring", daring, NULL, 1, 0}, -#if N_FDC > 1 - {"two_fdc", NULL, &FDC2, 0x370, 0}, - {"one_fdc", NULL, &FDC2, 0, 0}, -#endif - {"thinkpad", floppy_set_flags, NULL, 1, FD_INVERTED_DCL}, - {"broken_dcl", floppy_set_flags, NULL, 1, FD_BROKEN_DCL}, - {"messages", floppy_set_flags, NULL, 1, FTD_MSG}, - {"silent_dcl_clear", floppy_set_flags, NULL, 1, FD_SILENT_DCL_CLEAR}, - {"debug", floppy_set_flags, NULL, 1, FD_DEBUG}, - {"nodma", NULL, &can_use_virtual_dma, 1, 0}, - {"omnibook", NULL, &can_use_virtual_dma, 1, 0}, - {"yesdma", NULL, &can_use_virtual_dma, 0, 0}, - {"fifo_depth", NULL, &fifo_depth, 0xa, 0}, - {"nofifo", NULL, &no_fifo, 0x20, 0}, - {"usefifo", NULL, &no_fifo, 0, 0}, - {"cmos", set_cmos, NULL, 0, 0}, - {"slow", NULL, &slow_floppy, 1, 0}, - {"unexpected_interrupts", NULL, &print_unex, 1, 0}, - {"no_unexpected_interrupts", NULL, &print_unex, 0, 0}, - {"L40SX", NULL, &print_unex, 0, 0} - - EXTRA_FLOPPY_PARAMS -}; - -static int __init floppy_setup(char *str) -{ - int i; - int param; - int ints[11]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - if (str) { - for (i = 0; i < ARRAY_SIZE(config_params); i++) { - if (strcmp(str, config_params[i].name) == 0) { - if (ints[0]) - param = ints[1]; - else - param = config_params[i].def_param; - if (config_params[i].fn) - config_params[i].fn(ints, param, - config_params[i]. - param2); - if (config_params[i].var) { - DPRINT("%s=%d\n", str, param); - *config_params[i].var = param; - } - return 1; - } - } - } - if (str) { - DPRINT("unknown floppy option [%s]\n", str); - - DPRINT("allowed options are:"); - for (i = 0; i < ARRAY_SIZE(config_params); i++) - pr_cont(" %s", config_params[i].name); - pr_cont("\n"); - } else - DPRINT("botched floppy option\n"); - DPRINT("Read Documentation/blockdev/floppy.txt\n"); - return 0; -} - -static int have_no_fdc = -ENODEV; - -static ssize_t floppy_cmos_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *p = to_platform_device(dev); - int drive; - - drive = p->id; - return sprintf(buf, "%X\n", UDP->cmos); -} - -static DEVICE_ATTR(cmos, 0444, floppy_cmos_show, NULL); - -static struct attribute *floppy_dev_attrs[] = { - &dev_attr_cmos.attr, - NULL -}; - -ATTRIBUTE_GROUPS(floppy_dev); - -static void floppy_device_release(struct device *dev) -{} - -static int floppy_resume(struct device *dev) -{ - int fdc; - - for (fdc = 0; fdc < N_FDC; fdc++) - if (FDCS->address != -1) - user_reset_fdc(-1, FD_RESET_ALWAYS, false); - - return 0; -} - -static const struct dev_pm_ops floppy_pm_ops = { - .resume = floppy_resume, - .restore = floppy_resume, -}; - -static struct platform_driver floppy_driver = { - .driver = { - .name = "floppy", - .pm = &floppy_pm_ops, - }, -}; - -static const struct blk_mq_ops floppy_mq_ops = { - .queue_rq = floppy_queue_rq, -}; - -static struct platform_device floppy_device[N_DRIVE]; - -static bool floppy_available(int drive) -{ - if (!(allowed_drive_mask & (1 << drive))) - return false; - if (fdc_state[FDC(drive)].version == FDC_NONE) - return false; - return true; -} - -static struct kobject *floppy_find(dev_t dev, int *part, void *data) -{ - int drive = (*part & 3) | ((*part & 0x80) >> 5); - if (drive >= N_DRIVE || !floppy_available(drive)) - return NULL; - if (((*part >> 2) & 0x1f) >= ARRAY_SIZE(floppy_type)) - return NULL; - *part = 0; - return get_disk_and_module(disks[drive]); -} - -static int __init do_floppy_init(void) -{ - int i, unit, drive, err; - - set_debugt(); - interruptjiffies = resultjiffies = jiffies; - -#if defined(CONFIG_PPC) - if (check_legacy_ioport(FDC1)) - return -ENODEV; -#endif - - raw_cmd = NULL; - - floppy_wq = alloc_ordered_workqueue("floppy", 0); - if (!floppy_wq) - return -ENOMEM; - - for (drive = 0; drive < N_DRIVE; drive++) { - disks[drive] = alloc_disk(1); - if (!disks[drive]) { - err = -ENOMEM; - goto out_put_disk; - } - - disks[drive]->queue = blk_mq_init_sq_queue(&tag_sets[drive], - &floppy_mq_ops, 2, - BLK_MQ_F_SHOULD_MERGE); - if (IS_ERR(disks[drive]->queue)) { - err = PTR_ERR(disks[drive]->queue); - disks[drive]->queue = NULL; - goto out_put_disk; - } - - blk_queue_bounce_limit(disks[drive]->queue, BLK_BOUNCE_HIGH); - blk_queue_max_hw_sectors(disks[drive]->queue, 64); - disks[drive]->major = FLOPPY_MAJOR; - disks[drive]->first_minor = TOMINOR(drive); - disks[drive]->fops = &floppy_fops; - disks[drive]->events = DISK_EVENT_MEDIA_CHANGE; - sprintf(disks[drive]->disk_name, "fd%d", drive); - - timer_setup(&motor_off_timer[drive], motor_off_callback, 0); - } - - err = register_blkdev(FLOPPY_MAJOR, "fd"); - if (err) - goto out_put_disk; - - err = platform_driver_register(&floppy_driver); - if (err) - goto out_unreg_blkdev; - - blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE, - floppy_find, NULL, NULL); - - for (i = 0; i < 256; i++) - if (ITYPE(i)) - floppy_sizes[i] = floppy_type[ITYPE(i)].size; - else - floppy_sizes[i] = MAX_DISK_SIZE << 1; - - reschedule_timeout(MAXTIMEOUT, "floppy init"); - config_types(); - - for (i = 0; i < N_FDC; i++) { - fdc = i; - memset(FDCS, 0, sizeof(*FDCS)); - FDCS->dtr = -1; - FDCS->dor = 0x4; -#if defined(__sparc__) || defined(__mc68000__) - /*sparcs/sun3x don't have a DOR reset which we can fall back on to */ -#ifdef __mc68000__ - if (MACH_IS_SUN3X) -#endif - FDCS->version = FDC_82072A; -#endif - } - - use_virtual_dma = can_use_virtual_dma & 1; - fdc_state[0].address = FDC1; - if (fdc_state[0].address == -1) { - cancel_delayed_work(&fd_timeout); - err = -ENODEV; - goto out_unreg_region; - } -#if N_FDC > 1 - fdc_state[1].address = FDC2; -#endif - - fdc = 0; /* reset fdc in case of unexpected interrupt */ - err = floppy_grab_irq_and_dma(); - if (err) { - cancel_delayed_work(&fd_timeout); - err = -EBUSY; - goto out_unreg_region; - } - - /* initialise drive state */ - for (drive = 0; drive < N_DRIVE; drive++) { - memset(UDRS, 0, sizeof(*UDRS)); - memset(UDRWE, 0, sizeof(*UDRWE)); - set_bit(FD_DISK_NEWCHANGE_BIT, &UDRS->flags); - set_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); - set_bit(FD_VERIFY_BIT, &UDRS->flags); - UDRS->fd_device = -1; - floppy_track_buffer = NULL; - max_buffer_sectors = 0; - } - /* - * Small 10 msec delay to let through any interrupt that - * initialization might have triggered, to not - * confuse detection: - */ - msleep(10); - - for (i = 0; i < N_FDC; i++) { - fdc = i; - FDCS->driver_version = FD_DRIVER_VERSION; - for (unit = 0; unit < 4; unit++) - FDCS->track[unit] = 0; - if (FDCS->address == -1) - continue; - FDCS->rawcmd = 2; - if (user_reset_fdc(-1, FD_RESET_ALWAYS, false)) { - /* free ioports reserved by floppy_grab_irq_and_dma() */ - floppy_release_regions(fdc); - FDCS->address = -1; - FDCS->version = FDC_NONE; - continue; - } - /* Try to determine the floppy controller type */ - FDCS->version = get_fdc_version(); - if (FDCS->version == FDC_NONE) { - /* free ioports reserved by floppy_grab_irq_and_dma() */ - floppy_release_regions(fdc); - FDCS->address = -1; - continue; - } - if (can_use_virtual_dma == 2 && FDCS->version < FDC_82072A) - can_use_virtual_dma = 0; - - have_no_fdc = 0; - /* Not all FDCs seem to be able to handle the version command - * properly, so force a reset for the standard FDC clones, - * to avoid interrupt garbage. - */ - user_reset_fdc(-1, FD_RESET_ALWAYS, false); - } - fdc = 0; - cancel_delayed_work(&fd_timeout); - current_drive = 0; - initialized = true; - if (have_no_fdc) { - DPRINT("no floppy controllers found\n"); - err = have_no_fdc; - goto out_release_dma; - } - - for (drive = 0; drive < N_DRIVE; drive++) { - if (!floppy_available(drive)) - continue; - - floppy_device[drive].name = floppy_device_name; - floppy_device[drive].id = drive; - floppy_device[drive].dev.release = floppy_device_release; - floppy_device[drive].dev.groups = floppy_dev_groups; - - err = platform_device_register(&floppy_device[drive]); - if (err) - goto out_remove_drives; - - /* to be cleaned up... */ - disks[drive]->private_data = (void *)(long)drive; - disks[drive]->flags |= GENHD_FL_REMOVABLE; - device_add_disk(&floppy_device[drive].dev, disks[drive], NULL); - } - - return 0; - -out_remove_drives: - while (drive--) { - if (floppy_available(drive)) { - del_gendisk(disks[drive]); - platform_device_unregister(&floppy_device[drive]); - } - } -out_release_dma: - if (atomic_read(&usage_count)) - floppy_release_irq_and_dma(); -out_unreg_region: - blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); - platform_driver_unregister(&floppy_driver); -out_unreg_blkdev: - unregister_blkdev(FLOPPY_MAJOR, "fd"); -out_put_disk: - destroy_workqueue(floppy_wq); - for (drive = 0; drive < N_DRIVE; drive++) { - if (!disks[drive]) - break; - if (disks[drive]->queue) { - del_timer_sync(&motor_off_timer[drive]); - blk_cleanup_queue(disks[drive]->queue); - disks[drive]->queue = NULL; - blk_mq_free_tag_set(&tag_sets[drive]); - } - put_disk(disks[drive]); - } - return err; -} - -#ifndef MODULE -static __init void floppy_async_init(void *data, async_cookie_t cookie) -{ - do_floppy_init(); -} -#endif - -static int __init floppy_init(void) -{ -#ifdef MODULE - return do_floppy_init(); -#else - /* Don't hold up the bootup by the floppy initialization */ - async_schedule(floppy_async_init, NULL); - return 0; -#endif -} - -static const struct io_region { - int offset; - int size; -} io_regions[] = { - { 2, 1 }, - /* address + 3 is sometimes reserved by pnp bios for motherboard */ - { 4, 2 }, - /* address + 6 is reserved, and may be taken by IDE. - * Unfortunately, Adaptec doesn't know this :-(, */ - { 7, 1 }, -}; - -static void floppy_release_allocated_regions(int fdc, const struct io_region *p) -{ - while (p != io_regions) { - p--; - release_region(FDCS->address + p->offset, p->size); - } -} - -#define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)])) - -static int floppy_request_regions(int fdc) -{ - const struct io_region *p; - - for (p = io_regions; p < ARRAY_END(io_regions); p++) { - if (!request_region(FDCS->address + p->offset, - p->size, "floppy")) { - DPRINT("Floppy io-port 0x%04lx in use\n", - FDCS->address + p->offset); - floppy_release_allocated_regions(fdc, p); - return -EBUSY; - } - } - return 0; -} - -static void floppy_release_regions(int fdc) -{ - floppy_release_allocated_regions(fdc, ARRAY_END(io_regions)); -} - -static int floppy_grab_irq_and_dma(void) -{ - if (atomic_inc_return(&usage_count) > 1) - return 0; - - /* - * We might have scheduled a free_irq(), wait it to - * drain first: - */ - flush_workqueue(floppy_wq); - - if (fd_request_irq()) { - DPRINT("Unable to grab IRQ%d for the floppy driver\n", - FLOPPY_IRQ); - atomic_dec(&usage_count); - return -1; - } - if (fd_request_dma()) { - DPRINT("Unable to grab DMA%d for the floppy driver\n", - FLOPPY_DMA); - if (can_use_virtual_dma & 2) - use_virtual_dma = can_use_virtual_dma = 1; - if (!(can_use_virtual_dma & 1)) { - fd_free_irq(); - atomic_dec(&usage_count); - return -1; - } - } - - for (fdc = 0; fdc < N_FDC; fdc++) { - if (FDCS->address != -1) { - if (floppy_request_regions(fdc)) - goto cleanup; - } - } - for (fdc = 0; fdc < N_FDC; fdc++) { - if (FDCS->address != -1) { - reset_fdc_info(1); - fd_outb(FDCS->dor, FD_DOR); - } - } - fdc = 0; - set_dor(0, ~0, 8); /* avoid immediate interrupt */ - - for (fdc = 0; fdc < N_FDC; fdc++) - if (FDCS->address != -1) - fd_outb(FDCS->dor, FD_DOR); - /* - * The driver will try and free resources and relies on us - * to know if they were allocated or not. - */ - fdc = 0; - irqdma_allocated = 1; - return 0; -cleanup: - fd_free_irq(); - fd_free_dma(); - while (--fdc >= 0) - floppy_release_regions(fdc); - atomic_dec(&usage_count); - return -1; -} - -static void floppy_release_irq_and_dma(void) -{ - int old_fdc; -#ifndef __sparc__ - int drive; -#endif - long tmpsize; - unsigned long tmpaddr; - - if (!atomic_dec_and_test(&usage_count)) - return; - - if (irqdma_allocated) { - fd_disable_dma(); - fd_free_dma(); - fd_free_irq(); - irqdma_allocated = 0; - } - set_dor(0, ~0, 8); -#if N_FDC > 1 - set_dor(1, ~8, 0); -#endif - - if (floppy_track_buffer && max_buffer_sectors) { - tmpsize = max_buffer_sectors * 1024; - tmpaddr = (unsigned long)floppy_track_buffer; - floppy_track_buffer = NULL; - max_buffer_sectors = 0; - buffer_min = buffer_max = -1; - fd_dma_mem_free(tmpaddr, tmpsize); - } -#ifndef __sparc__ - for (drive = 0; drive < N_FDC * 4; drive++) - if (timer_pending(motor_off_timer + drive)) - pr_info("motor off timer %d still active\n", drive); -#endif - - if (delayed_work_pending(&fd_timeout)) - pr_info("floppy timer still active:%s\n", timeout_message); - if (delayed_work_pending(&fd_timer)) - pr_info("auxiliary floppy timer still active\n"); - if (work_pending(&floppy_work)) - pr_info("work still pending\n"); - old_fdc = fdc; - for (fdc = 0; fdc < N_FDC; fdc++) - if (FDCS->address != -1) - floppy_release_regions(fdc); - fdc = old_fdc; -} - -#ifdef MODULE - -static char *floppy; - -static void __init parse_floppy_cfg_string(char *cfg) -{ - char *ptr; - - while (*cfg) { - ptr = cfg; - while (*cfg && *cfg != ' ' && *cfg != '\t') - cfg++; - if (*cfg) { - *cfg = '\0'; - cfg++; - } - if (*ptr) - floppy_setup(ptr); - } -} - -static int __init floppy_module_init(void) -{ - if (floppy) - parse_floppy_cfg_string(floppy); - return floppy_init(); -} -module_init(floppy_module_init); - -static void __exit floppy_module_exit(void) -{ - int drive; - - blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); - unregister_blkdev(FLOPPY_MAJOR, "fd"); - platform_driver_unregister(&floppy_driver); - - destroy_workqueue(floppy_wq); - - for (drive = 0; drive < N_DRIVE; drive++) { - del_timer_sync(&motor_off_timer[drive]); - - if (floppy_available(drive)) { - del_gendisk(disks[drive]); - platform_device_unregister(&floppy_device[drive]); - } - blk_cleanup_queue(disks[drive]->queue); - blk_mq_free_tag_set(&tag_sets[drive]); - - /* - * These disks have not called add_disk(). Don't put down - * queue reference in put_disk(). - */ - if (!(allowed_drive_mask & (1 << drive)) || - fdc_state[FDC(drive)].version == FDC_NONE) - disks[drive]->queue = NULL; - - put_disk(disks[drive]); - } - - cancel_delayed_work_sync(&fd_timeout); - cancel_delayed_work_sync(&fd_timer); - - if (atomic_read(&usage_count)) - floppy_release_irq_and_dma(); - - /* eject disk, if any */ - fd_eject(0); -} - -module_exit(floppy_module_exit); - -module_param(floppy, charp, 0); -module_param(FLOPPY_IRQ, int, 0); -module_param(FLOPPY_DMA, int, 0); -MODULE_AUTHOR("Alain L. Knaff"); -MODULE_SUPPORTED_DEVICE("fd"); -MODULE_LICENSE("GPL"); - -/* This doesn't actually get used other than for module information */ -static const struct pnp_device_id floppy_pnpids[] = { - {"PNP0700", 0}, - {} -}; - -MODULE_DEVICE_TABLE(pnp, floppy_pnpids); - -#else - -__setup("floppy=", floppy_setup); -module_init(floppy_init) -#endif - -MODULE_ALIAS_BLOCKDEV_MAJOR(FLOPPY_MAJOR); diff --git a/test/bug-hunting/cve/CVE-2019-14494/README b/test/bug-hunting/cve/CVE-2019-14494/README deleted file mode 100644 index 4b8529faa97..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14494/README +++ /dev/null @@ -1,7 +0,0 @@ -Project: -Poppler - -Details: -https://nvd.nist.gov/vuln/detail/CVE-2019-14494 - - diff --git a/test/bug-hunting/cve/CVE-2019-14494/SplashOutputDev.cc b/test/bug-hunting/cve/CVE-2019-14494/SplashOutputDev.cc deleted file mode 100644 index 544f132da17..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14494/SplashOutputDev.cc +++ /dev/null @@ -1,4867 +0,0 @@ -//======================================================================== -// -// SplashOutputDev.cc -// -// Copyright 2003 Glyph & Cog, LLC -// -//======================================================================== - -//======================================================================== -// -// Modified under the Poppler project - http://poppler.freedesktop.org -// -// All changes made under the Poppler project to this file are licensed -// under GPL version 2 or later -// -// Copyright (C) 2005 Takashi Iwai -// Copyright (C) 2006 Stefan Schweizer -// Copyright (C) 2006-2019 Albert Astals Cid -// Copyright (C) 2006 Krzysztof Kowalczyk -// Copyright (C) 2006 Scott Turner -// Copyright (C) 2007 Koji Otani -// Copyright (C) 2009 Petr Gajdos -// Copyright (C) 2009-2016 Thomas Freitag -// Copyright (C) 2009 Carlos Garcia Campos -// Copyright (C) 2009, 2014-2016, 2019 William Bader -// Copyright (C) 2010 Patrick Spendrin -// Copyright (C) 2010 Brian Cameron -// Copyright (C) 2010 PaweÅ‚ Wiejacha -// Copyright (C) 2010 Christian Feuersänger -// Copyright (C) 2011 Andreas Hartmetz -// Copyright (C) 2011 Andrea Canciani -// Copyright (C) 2011, 2012, 2017 Adrian Johnson -// Copyright (C) 2013 Lu Wang -// Copyright (C) 2013 Li Junling -// Copyright (C) 2014 Ed Porras -// Copyright (C) 2014 Richard PALO -// Copyright (C) 2015 Tamas Szekeres -// Copyright (C) 2015 Kenji Uno -// Copyright (C) 2016 Takahiro Hashimoto -// Copyright (C) 2017 Even Rouault -// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich -// Copyright (C) 2018 Stefan Brüns -// Copyright (C) 2018 Adam Reichold -// Copyright (C) 2019 Christian Persch -// -// To see a description of the changes please see the Changelog file that -// came with your tarball or type make ChangeLog if you are building from git -// -//======================================================================== - -#include - -#include -#include -#include "goo/gfile.h" -#include "GlobalParams.h" -#include "Error.h" -#include "Object.h" -#include "Gfx.h" -#include "GfxFont.h" -#include "Page.h" -#include "PDFDoc.h" -#include "Link.h" -#include "FontEncodingTables.h" -#include "fofi/FoFiTrueType.h" -#include "splash/SplashBitmap.h" -#include "splash/SplashGlyphBitmap.h" -#include "splash/SplashPattern.h" -#include "splash/SplashScreen.h" -#include "splash/SplashPath.h" -#include "splash/SplashState.h" -#include "splash/SplashErrorCodes.h" -#include "splash/SplashFontEngine.h" -#include "splash/SplashFont.h" -#include "splash/SplashFontFile.h" -#include "splash/SplashFontFileID.h" -#include "splash/Splash.h" -#include "SplashOutputDev.h" -#include - -static const double s_minLineWidth = 0.0; - -static inline void convertGfxColor(SplashColorPtr dest, - SplashColorMode colorMode, - GfxColorSpace *colorSpace, - GfxColor *src) { - SplashColor color; - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - - // make gcc happy - color[0] = color[1] = color[2] = 0; -#ifdef SPLASH_CMYK - color[3] = 0; -#endif - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - colorSpace->getGray(src, &gray); - color[0] = colToByte(gray); - break; - case splashModeXBGR8: - color[3] = 255; - // fallthrough - case splashModeBGR8: - case splashModeRGB8: - colorSpace->getRGB(src, &rgb); - color[0] = colToByte(rgb.r); - color[1] = colToByte(rgb.g); - color[2] = colToByte(rgb.b); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - colorSpace->getCMYK(src, &cmyk); - color[0] = colToByte(cmyk.c); - color[1] = colToByte(cmyk.m); - color[2] = colToByte(cmyk.y); - color[3] = colToByte(cmyk.k); - break; - case splashModeDeviceN8: - colorSpace->getDeviceN(src, &deviceN); - for (int i = 0; i < SPOT_NCOMPS + 4; i++) - color[i] = colToByte(deviceN.c[i]); - break; -#endif - } - splashColorCopy(dest, color); -} - -// Copy a color according to the color mode. -// Use convertGfxShortColor() below when the destination is a bitmap -// to avoid overwriting cells. -// Calling this in SplashGouraudPattern::getParameterizedColor() fixes bug 90570. -// Use convertGfxColor() above when the destination is an array of SPOT_NCOMPS+4 bytes, -// to ensure that everything is initialized. - -static inline void convertGfxShortColor(SplashColorPtr dest, - SplashColorMode colorMode, - GfxColorSpace *colorSpace, - GfxColor *src) { - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - { - GfxGray gray; - colorSpace->getGray(src, &gray); - dest[0] = colToByte(gray); - } - break; - case splashModeXBGR8: - dest[3] = 255; - // fallthrough - case splashModeBGR8: - case splashModeRGB8: - { - GfxRGB rgb; - colorSpace->getRGB(src, &rgb); - dest[0] = colToByte(rgb.r); - dest[1] = colToByte(rgb.g); - dest[2] = colToByte(rgb.b); - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - { - GfxCMYK cmyk; - colorSpace->getCMYK(src, &cmyk); - dest[0] = colToByte(cmyk.c); - dest[1] = colToByte(cmyk.m); - dest[2] = colToByte(cmyk.y); - dest[3] = colToByte(cmyk.k); - } - break; - case splashModeDeviceN8: - { - GfxColor deviceN; - colorSpace->getDeviceN(src, &deviceN); - for (int i = 0; i < SPOT_NCOMPS + 4; i++) - dest[i] = colToByte(deviceN.c[i]); - } - break; -#endif - } -} - -//------------------------------------------------------------------------ -// SplashGouraudPattern -//------------------------------------------------------------------------ -SplashGouraudPattern::SplashGouraudPattern(bool bDirectColorTranslationA, - GfxState *stateA, GfxGouraudTriangleShading *shadingA) { - state = stateA; - shading = shadingA; - bDirectColorTranslation = bDirectColorTranslationA; - gfxMode = shadingA->getColorSpace()->getMode(); -} - -SplashGouraudPattern::~SplashGouraudPattern() { -} - -void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) { - GfxColor src; - GfxColorSpace* srcColorSpace = shading->getColorSpace(); - int colorComps = 3; -#ifdef SPLASH_CMYK - if (mode == splashModeCMYK8) - colorComps=4; - else if (mode == splashModeDeviceN8) - colorComps=4 + SPOT_NCOMPS; -#endif - - shading->getParameterizedColor(colorinterp, &src); - - if (bDirectColorTranslation) { - for (int m = 0; m < colorComps; ++m) - dest[m] = colToByte(src.c[m]); - } else { - convertGfxShortColor(dest, mode, srcColorSpace, &src); - } -} - -//------------------------------------------------------------------------ -// SplashFunctionPattern -//------------------------------------------------------------------------ - -SplashFunctionPattern::SplashFunctionPattern(SplashColorMode colorModeA, GfxState *stateA, GfxFunctionShading *shadingA) -{ - Matrix ctm; - SplashColor defaultColor; - GfxColor srcColor; - const double *matrix = shadingA->getMatrix(); - - shading = shadingA; - state = stateA; - colorMode = colorModeA; - - state->getCTM(&ctm); - - double a1 = ctm.m[0]; - double b1 = ctm.m[1]; - double c1 = ctm.m[2]; - double d1 = ctm.m[3]; - - ctm.m[0] = matrix[0] * a1 + matrix[1] * c1; - ctm.m[1] = matrix[0] * b1 + matrix[1] * d1; - ctm.m[2] = matrix[2] * a1 + matrix[3] * c1; - ctm.m[3] = matrix[2] * b1 + matrix[3] * d1; - ctm.m[4] = matrix[4] * a1 + matrix[5] * c1 + ctm.m[4]; - ctm.m[5] = matrix[4] * b1 + matrix[5] * d1 + ctm.m[5]; - ctm.invertTo(&ictm); - - gfxMode = shadingA->getColorSpace()->getMode(); - shadingA->getColorSpace()->getDefaultColor(&srcColor); - shadingA->getDomain(&xMin, &yMin, &xMax, &yMax); - convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); -} - -SplashFunctionPattern::~SplashFunctionPattern() { -} - -bool SplashFunctionPattern::getColor(int x, int y, SplashColorPtr c) { - GfxColor gfxColor; - double xc, yc; - - ictm.transform(x, y, &xc, &yc); - if (xc < xMin || xc > xMax || yc < yMin || yc > yMax) return false; - shading->getColor(xc, yc, &gfxColor); - convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor); - return true; -} - -//------------------------------------------------------------------------ -// SplashUnivariatePattern -//------------------------------------------------------------------------ - -SplashUnivariatePattern::SplashUnivariatePattern(SplashColorMode colorModeA, GfxState *stateA, GfxUnivariateShading *shadingA) { - Matrix ctm; - double xMin, yMin, xMax, yMax; - - shading = shadingA; - state = stateA; - colorMode = colorModeA; - - state->getCTM(&ctm); - ctm.invertTo(&ictm); - - // get the function domain - t0 = shading->getDomain0(); - t1 = shading->getDomain1(); - dt = t1 - t0; - - stateA->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); - shadingA->setupCache(&ctm, xMin, yMin, xMax, yMax); - gfxMode = shadingA->getColorSpace()->getMode(); -} - -SplashUnivariatePattern::~SplashUnivariatePattern() { -} - -bool SplashUnivariatePattern::getColor(int x, int y, SplashColorPtr c) { - GfxColor gfxColor; - double xc, yc, t; - - ictm.transform(x, y, &xc, &yc); - if (! getParameter (xc, yc, &t)) - return false; - - const int filled = shading->getColor(t, &gfxColor); - if (unlikely(filled < shading->getColorSpace()->getNComps())) { - for (int i = filled; i < shading->getColorSpace()->getNComps(); ++i) - gfxColor.c[i] = 0; - } - convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor); - return true; -} - -bool SplashUnivariatePattern::testPosition(int x, int y) { - double xc, yc, t; - - ictm.transform(x, y, &xc, &yc); - if (! getParameter (xc, yc, &t)) - return false; - return (t0 < t1) ? (t > t0 && t < t1) : (t > t1 && t < t0); -} - - -//------------------------------------------------------------------------ -// SplashRadialPattern -//------------------------------------------------------------------------ -#define RADIAL_EPSILON (1. / 1024 / 1024) - -SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA): - SplashUnivariatePattern(colorModeA, stateA, shadingA) -{ - SplashColor defaultColor; - GfxColor srcColor; - - shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr); - dx -= x0; - dy -= y0; - dr -= r0; - a = dx*dx + dy*dy - dr*dr; - if (fabs(a) > RADIAL_EPSILON) - inva = 1.0 / a; - shadingA->getColorSpace()->getDefaultColor(&srcColor); - convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); -} - -SplashRadialPattern::~SplashRadialPattern() { -} - -bool SplashRadialPattern::getParameter(double xs, double ys, double *t) { - double b, c, s0, s1; - - // We want to solve this system of equations: - // - // 1. (x - xc(s))^2 + (y -yc(s))^2 = rc(s)^2 - // 2. xc(s) = x0 + s * (x1 - xo) - // 3. yc(s) = y0 + s * (y1 - yo) - // 4. rc(s) = r0 + s * (r1 - ro) - // - // To simplify the system a little, we translate - // our coordinates to have the origin in (x0,y0) - - xs -= x0; - ys -= y0; - - // Then we have to solve the equation: - // A*s^2 - 2*B*s + C = 0 - // where - // A = dx^2 + dy^2 - dr^2 - // B = xs*dx + ys*dy + r0*dr - // C = xs^2 + ys^2 - r0^2 - - b = xs*dx + ys*dy + r0*dr; - c = xs*xs + ys*ys - r0*r0; - - if (fabs(a) <= RADIAL_EPSILON) { - // A is 0, thus the equation simplifies to: - // -2*B*s + C = 0 - // If B is 0, we can either have no solution or an indeterminate - // equation, thus we behave as if we had an invalid solution - if (fabs(b) <= RADIAL_EPSILON) - return false; - - s0 = s1 = 0.5 * c / b; - } else { - double d; - - d = b*b - a*c; - if (d < 0) - return false; - - d = sqrt (d); - s0 = b + d; - s1 = b - d; - - // If A < 0, one of the two solutions will have negative radius, - // thus it will be ignored. Otherwise we know that s1 <= s0 - // (because d >=0 implies b - d <= b + d), so if both are valid it - // will be the true solution. - s0 *= inva; - s1 *= inva; - } - - if (r0 + s0 * dr >= 0) { - if (0 <= s0 && s0 <= 1) { - *t = t0 + dt * s0; - return true; - } else if (s0 < 0 && shading->getExtend0()) { - *t = t0; - return true; - } else if (s0 > 1 && shading->getExtend1()) { - *t = t1; - return true; - } - } - - if (r0 + s1 * dr >= 0) { - if (0 <= s1 && s1 <= 1) { - *t = t0 + dt * s1; - return true; - } else if (s1 < 0 && shading->getExtend0()) { - *t = t0; - return true; - } else if (s1 > 1 && shading->getExtend1()) { - *t = t1; - return true; - } - } - - return false; -} - -#undef RADIAL_EPSILON - -//------------------------------------------------------------------------ -// SplashAxialPattern -//------------------------------------------------------------------------ - -SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA): - SplashUnivariatePattern(colorModeA, stateA, shadingA) -{ - SplashColor defaultColor; - GfxColor srcColor; - - shadingA->getCoords(&x0, &y0, &x1, &y1); - dx = x1 - x0; - dy = y1 - y0; - const double mul_denominator = (dx * dx + dy * dy); - if (unlikely(mul_denominator == 0)) { - mul = 0; - } else { - mul = 1 / mul_denominator; - } - shadingA->getColorSpace()->getDefaultColor(&srcColor); - convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); -} - -SplashAxialPattern::~SplashAxialPattern() { -} - -bool SplashAxialPattern::getParameter(double xc, double yc, double *t) { - double s; - - xc -= x0; - yc -= y0; - - s = (xc * dx + yc * dy) * mul; - if (0 <= s && s <= 1) { - *t = t0 + dt * s; - } else if (s < 0 && shading->getExtend0()) { - *t = t0; - } else if (s > 1 && shading->getExtend1()) { - *t = t1; - } else { - return false; - } - - return true; -} - -//------------------------------------------------------------------------ -// Type 3 font cache size parameters -#define type3FontCacheAssoc 8 -#define type3FontCacheMaxSets 8 -#define type3FontCacheSize (128*1024) - -//------------------------------------------------------------------------ -// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. -static inline unsigned char div255(int x) { - return (unsigned char)((x + (x >> 8) + 0x80) >> 8); -} - -//------------------------------------------------------------------------ -// Blend functions -//------------------------------------------------------------------------ - -static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = (dest[i] * src[i]) / 255; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] < 0x80 - ? (src[i] * 2 * dest[i]) / 255 - : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] < src[i] ? dest[i] : src[i]; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] > src[i] ? dest[i] : src[i]; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, - SplashColorMode cm) { - int i, x; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - if (src[i] == 255) { - blend[i] = 255; - } else { - x = (dest[i] * 255) / (255 - src[i]); - blend[i] = x <= 255 ? x : 255; - } - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i, x; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - if (src[i] == 0) { - blend[i] = 0; - } else { - x = ((255 - dest[i]) * 255) / src[i]; - blend[i] = x <= 255 ? 255 - x : 0; - } - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = src[i] < 0x80 - ? (dest[i] * 2 * src[i]) / 255 - : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i, x; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - if (src[i] < 0x80) { - blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / (255 * 255); - } else { - if (dest[i] < 0x40) { - x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) + 4 * 255) * dest[i]) / 255; - } else { - x = (int)sqrt(255.0 * dest[i]); - } - blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255; - } - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } -#endif -} - -static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, - SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i]; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } - if (cm == splashModeDeviceN8) { - for (i = 4; i < splashColorModeNComps[cm]; ++i) { - if (dest[i] == 0 && src[i] == 0) - blend[i] = 0; - } - } -#endif -} - -static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - int i; - -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - } - } -#endif - { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255; - } - } -#ifdef SPLASH_CMYK - if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { - for (i = 0; i < splashColorModeNComps[cm]; ++i) { - dest[i] = 255 - dest[i]; - src[i] = 255 - src[i]; - blend[i] = 255 - blend[i]; - } - } - if (cm == splashModeDeviceN8) { - for (i = 4; i < splashColorModeNComps[cm]; ++i) { - if (dest[i] == 0 && src[i] == 0) - blend[i] = 0; - } - } -#endif -} - -static int getLum(int r, int g, int b) { - return (int)(0.3 * r + 0.59 * g + 0.11 * b); -} - -static int getSat(int r, int g, int b) { - int rgbMin, rgbMax; - - rgbMin = rgbMax = r; - if (g < rgbMin) { - rgbMin = g; - } else if (g > rgbMax) { - rgbMax = g; - } - if (b < rgbMin) { - rgbMin = b; - } else if (b > rgbMax) { - rgbMax = b; - } - return rgbMax - rgbMin; -} - -static void clipColor(int rIn, int gIn, int bIn, - unsigned char *rOut, unsigned char *gOut, unsigned char *bOut) { - int lum, rgbMin, rgbMax; - - lum = getLum(rIn, gIn, bIn); - rgbMin = rgbMax = rIn; - if (gIn < rgbMin) { - rgbMin = gIn; - } else if (gIn > rgbMax) { - rgbMax = gIn; - } - if (bIn < rgbMin) { - rgbMin = bIn; - } else if (bIn > rgbMax) { - rgbMax = bIn; - } - if (rgbMin < 0) { - *rOut = (unsigned char)(lum + ((rIn - lum) * lum) / (lum - rgbMin)); - *gOut = (unsigned char)(lum + ((gIn - lum) * lum) / (lum - rgbMin)); - *bOut = (unsigned char)(lum + ((bIn - lum) * lum) / (lum - rgbMin)); - } else if (rgbMax > 255) { - *rOut = (unsigned char)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum)); - *gOut = (unsigned char)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum)); - *bOut = (unsigned char)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum)); - } else { - *rOut = rIn; - *gOut = gIn; - *bOut = bIn; - } -} - -static void setLum(unsigned char rIn, unsigned char gIn, unsigned char bIn, int lum, - unsigned char *rOut, unsigned char *gOut, unsigned char *bOut) { - int d; - - d = lum - getLum(rIn, gIn, bIn); - clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut); -} - -static void setSat(unsigned char rIn, unsigned char gIn, unsigned char bIn, int sat, - unsigned char *rOut, unsigned char *gOut, unsigned char *bOut) { - int rgbMin, rgbMid, rgbMax; - unsigned char *minOut, *midOut, *maxOut; - - if (rIn < gIn) { - rgbMin = rIn; minOut = rOut; - rgbMid = gIn; midOut = gOut; - } else { - rgbMin = gIn; minOut = gOut; - rgbMid = rIn; midOut = rOut; - } - if (bIn > rgbMid) { - rgbMax = bIn; maxOut = bOut; - } else if (bIn > rgbMin) { - rgbMax = rgbMid; maxOut = midOut; - rgbMid = bIn; midOut = bOut; - } else { - rgbMax = rgbMid; maxOut = midOut; - rgbMid = rgbMin; midOut = minOut; - rgbMin = bIn; minOut = bOut; - } - if (rgbMax > rgbMin) { - *midOut = (unsigned char)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin); - *maxOut = (unsigned char)sat; - } else { - *midOut = *maxOut = 0; - } - *minOut = 0; -} - -static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { - unsigned char r0, g0, b0; -#ifdef SPLASH_CMYK - unsigned char r1, g1, b1; - int i; - SplashColor src2, dest2; -#endif - - switch (cm) { - case splashModeMono1: - case splashModeMono8: - blend[0] = dest[0]; - break; - case splashModeXBGR8: - src[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), - &r0, &g0, &b0); - setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), - &blend[0], &blend[1], &blend[2]); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - for (i = 0; i < 4; i++) { - // convert to additive - src2[i] = 0xff - src[i]; - dest2[i] = 0xff - dest[i]; - } - // NB: inputs have already been converted to additive mode - setSat(src2[0], src2[1], src2[2], getSat(dest2[0], dest2[1], dest2[2]), - &r0, &g0, &b0); - setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]), - &r1, &g1, &b1); - blend[0] = r1; - blend[1] = g1; - blend[2] = b1; - blend[3] = dest2[3]; - for (i = 0; i < 4; i++) { - // convert back to subtractive - blend[i] = 0xff - blend[i]; - } - break; -#endif - } -} - -static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, - SplashColorMode cm) { - unsigned char r0, g0, b0; -#ifdef SPLASH_CMYK - unsigned char r1, g1, b1; - int i; - SplashColor src2, dest2; -#endif - - switch (cm) { - case splashModeMono1: - case splashModeMono8: - blend[0] = dest[0]; - break; - case splashModeXBGR8: - src[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), - &r0, &g0, &b0); - setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), - &blend[0], &blend[1], &blend[2]); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - for (i = 0; i < 4; i++) { - // convert to additive - src2[i] = 0xff - src[i]; - dest2[i] = 0xff - dest[i]; - } - setSat(dest2[0], dest2[1], dest2[2], getSat(src2[0], src2[1], src2[2]), - &r0, &g0, &b0); - setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]), - &r1, &g1, &b1); - blend[0] = r1; - blend[1] = g1; - blend[2] = b1; - blend[3] = dest2[3]; - for (i = 0; i < 4; i++) { - // convert back to subtractive - blend[i] = 0xff - blend[i]; - } - break; -#endif - } -} - -static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { -#ifdef SPLASH_CMYK - unsigned char r, g, b; - int i; - SplashColor src2, dest2; -#endif - - switch (cm) { - case splashModeMono1: - case splashModeMono8: - blend[0] = dest[0]; - break; - case splashModeXBGR8: - src[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), - &blend[0], &blend[1], &blend[2]); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - for (i = 0; i < 4; i++) { - // convert to additive - src2[i] = 0xff - src[i]; - dest2[i] = 0xff - dest[i]; - } - setLum(src2[0], src2[1], src2[2], getLum(dest2[0], dest2[1], dest2[2]), - &r, &g, &b); - blend[0] = r; - blend[1] = g; - blend[2] = b; - blend[3] = dest2[3]; - for (i = 0; i < 4; i++) { - // convert back to subtractive - blend[i] = 0xff - blend[i]; - } - break; -#endif - } -} - -static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, - SplashColorMode cm) { -#ifdef SPLASH_CMYK - unsigned char r, g, b; - int i; - SplashColor src2, dest2; -#endif - - switch (cm) { - case splashModeMono1: - case splashModeMono8: - blend[0] = dest[0]; - break; - case splashModeXBGR8: - src[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), - &blend[0], &blend[1], &blend[2]); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - for (i = 0; i < 4; i++) { - // convert to additive - src2[i] = 0xff - src[i]; - dest2[i] = 0xff - dest[i]; - } - setLum(dest2[0], dest2[1], dest2[2], getLum(src2[0], src2[1], src2[2]), - &r, &g, &b); - blend[0] = r; - blend[1] = g; - blend[2] = b; - blend[3] = src2[3]; - for (i = 0; i < 4; i++) { - // convert back to subtractive - blend[i] = 0xff - blend[i]; - } - break; -#endif - } -} - -// NB: This must match the GfxBlendMode enum defined in GfxState.h. -static const SplashBlendFunc splashOutBlendFuncs[] = { - nullptr, - &splashOutBlendMultiply, - &splashOutBlendScreen, - &splashOutBlendOverlay, - &splashOutBlendDarken, - &splashOutBlendLighten, - &splashOutBlendColorDodge, - &splashOutBlendColorBurn, - &splashOutBlendHardLight, - &splashOutBlendSoftLight, - &splashOutBlendDifference, - &splashOutBlendExclusion, - &splashOutBlendHue, - &splashOutBlendSaturation, - &splashOutBlendColor, - &splashOutBlendLuminosity -}; - -//------------------------------------------------------------------------ -// SplashOutFontFileID -//------------------------------------------------------------------------ - -class SplashOutFontFileID: public SplashFontFileID { -public: - - SplashOutFontFileID(const Ref *rA) { r = *rA; } - - ~SplashOutFontFileID() {} - - bool matches(SplashFontFileID *id) override { - return ((SplashOutFontFileID *)id)->r == r; - } - -private: - - Ref r; -}; - -//------------------------------------------------------------------------ -// T3FontCache -//------------------------------------------------------------------------ - -struct T3FontCacheTag { - unsigned short code; - unsigned short mru; // valid bit (0x8000) and MRU index -}; - -class T3FontCache { -public: - - T3FontCache(const Ref *fontID, double m11A, double m12A, - double m21A, double m22A, - int glyphXA, int glyphYA, int glyphWA, int glyphHA, - bool aa, bool validBBoxA); - ~T3FontCache(); - T3FontCache(const T3FontCache &) = delete; - T3FontCache& operator=(const T3FontCache &) = delete; - bool matches(const Ref *idA, double m11A, double m12A, - double m21A, double m22A) - { return fontID == *idA && - m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; } - - Ref fontID; // PDF font ID - double m11, m12, m21, m22; // transform matrix - int glyphX, glyphY; // pixel offset of glyph bitmaps - int glyphW, glyphH; // size of glyph bitmaps, in pixels - bool validBBox; // false if the bbox was [0 0 0 0] - int glyphSize; // size of glyph bitmaps, in bytes - int cacheSets; // number of sets in cache - int cacheAssoc; // cache associativity (glyphs per set) - unsigned char *cacheData; // glyph pixmap cache - T3FontCacheTag *cacheTags; // cache tags, i.e., char codes -}; - -T3FontCache::T3FontCache(const Ref *fontIDA, double m11A, double m12A, - double m21A, double m22A, - int glyphXA, int glyphYA, int glyphWA, int glyphHA, - bool validBBoxA, bool aa) { - - fontID = *fontIDA; - m11 = m11A; - m12 = m12A; - m21 = m21A; - m22 = m22A; - glyphX = glyphXA; - glyphY = glyphYA; - glyphW = glyphWA; - glyphH = glyphHA; - validBBox = validBBoxA; - // sanity check for excessively large glyphs (which most likely - // indicate an incorrect BBox) - if (glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0 || glyphW * glyphH > 100000) { - glyphW = glyphH = 100; - validBBox = false; - } - if (aa) { - glyphSize = glyphW * glyphH; - } else { - glyphSize = ((glyphW + 7) >> 3) * glyphH; - } - cacheAssoc = type3FontCacheAssoc; - for (cacheSets = type3FontCacheMaxSets; - cacheSets > 1 && - cacheSets * cacheAssoc * glyphSize > type3FontCacheSize; - cacheSets >>= 1) ; - if (glyphSize < 10485760 / cacheAssoc / cacheSets) { - cacheData = (unsigned char *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize); - } else { - error(errSyntaxWarning, -1, "Not creating cacheData for T3FontCache, it asked for too much memory.\n" - " This could teoretically result in wrong rendering,\n" - " but most probably the document is bogus.\n" - " Please report a bug if you think the rendering may be wrong because of this."); - cacheData = nullptr; - } - if (cacheData != nullptr) - { - cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc, - sizeof(T3FontCacheTag)); - for (int i = 0; i < cacheSets * cacheAssoc; ++i) { - cacheTags[i].mru = i & (cacheAssoc - 1); - } - } - else - { - cacheTags = nullptr; - } -} - -T3FontCache::~T3FontCache() { - gfree(cacheData); - gfree(cacheTags); -} - -struct T3GlyphStack { - unsigned short code; // character code - - bool haveDx; // set after seeing a d0/d1 operator - bool doNotCache; // set if we see a gsave/grestore before - // the d0/d1 - - //----- cache info - T3FontCache *cache; // font cache for the current font - T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph - unsigned char *cacheData; // pointer to cache data for the glyph - - //----- saved state - SplashBitmap *origBitmap; - Splash *origSplash; - double origCTM4, origCTM5; - - T3GlyphStack *next; // next object on stack -}; - -//------------------------------------------------------------------------ -// SplashTransparencyGroup -//------------------------------------------------------------------------ - -struct SplashTransparencyGroup { - int tx, ty; // translation coordinates - SplashBitmap *tBitmap; // bitmap for transparency group - SplashBitmap *softmask; // bitmap for softmasks - GfxColorSpace *blendingColorSpace; - bool isolated; - - //----- for knockout - SplashBitmap *shape; - bool knockout; - SplashCoord knockoutOpacity; - bool fontAA; - - //----- saved state - SplashBitmap *origBitmap; - Splash *origSplash; - - SplashTransparencyGroup *next; -}; - -//------------------------------------------------------------------------ -// SplashOutputDev -//------------------------------------------------------------------------ - -SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, - int bitmapRowPadA, - bool reverseVideoA, - SplashColorPtr paperColorA, - bool bitmapTopDownA, - SplashThinLineMode thinLineMode, - bool overprintPreviewA) { - colorMode = colorModeA; - bitmapRowPad = bitmapRowPadA; - bitmapTopDown = bitmapTopDownA; - bitmapUpsideDown = false; - fontAntialias = true; - vectorAntialias = true; - overprintPreview = overprintPreviewA; - enableFreeTypeHinting = false; - enableSlightHinting = false; - setupScreenParams(72.0, 72.0); - reverseVideo = reverseVideoA; - if (paperColorA != nullptr) { - splashColorCopy(paperColor, paperColorA); - } else { - splashClearColor(paperColor); - } - skipHorizText = false; - skipRotatedText = false; - keepAlphaChannel = paperColorA == nullptr; - - doc = nullptr; - - bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - splash = new Splash(bitmap, vectorAntialias, &screenParams); - splash->setMinLineWidth(s_minLineWidth); - splash->setThinLineMode(thinLineMode); - splash->clear(paperColor, 0); - - fontEngine = nullptr; - - nT3Fonts = 0; - t3GlyphStack = nullptr; - - font = nullptr; - needFontUpdate = false; - textClipPath = nullptr; - transpGroupStack = nullptr; - nestCount = 0; - xref = nullptr; -} - -void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) { - screenParams.size = -1; - screenParams.dotRadius = -1; - screenParams.gamma = (SplashCoord)1.0; - screenParams.blackThreshold = (SplashCoord)0.0; - screenParams.whiteThreshold = (SplashCoord)1.0; - - // use clustered dithering for resolution >= 300 dpi - // (compare to 299.9 to avoid floating point issues) - if (hDPI > 299.9 && vDPI > 299.9) { - screenParams.type = splashScreenStochasticClustered; - if (screenParams.size < 0) { - screenParams.size = 64; - } - if (screenParams.dotRadius < 0) { - screenParams.dotRadius = 2; - } - } else { - screenParams.type = splashScreenDispersed; - if (screenParams.size < 0) { - screenParams.size = 4; - } - } -} - -SplashOutputDev::~SplashOutputDev() { - int i; - - for (i = 0; i < nT3Fonts; ++i) { - delete t3FontCache[i]; - } - if (fontEngine) { - delete fontEngine; - } - if (splash) { - delete splash; - } - if (bitmap) { - delete bitmap; - } - delete textClipPath; -} - -void SplashOutputDev::startDoc(PDFDoc *docA) { - int i; - - doc = docA; - if (fontEngine) { - delete fontEngine; - } - fontEngine = new SplashFontEngine( - globalParams->getEnableFreeType(), - enableFreeTypeHinting, - enableSlightHinting, - getFontAntialias() && - colorMode != splashModeMono1); - for (i = 0; i < nT3Fonts; ++i) { - delete t3FontCache[i]; - } - nT3Fonts = 0; -} - -void SplashOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) { - int w, h; - SplashCoord mat[6]; - SplashColor color; - - xref = xrefA; - if (state) { - setupScreenParams(state->getHDPI(), state->getVDPI()); - w = (int)(state->getPageWidth() + 0.5); - if (w <= 0) { - w = 1; - } - h = (int)(state->getPageHeight() + 0.5); - if (h <= 0) { - h = 1; - } - } else { - w = h = 1; - } - SplashThinLineMode thinLineMode = splashThinLineDefault; - if (splash) { - thinLineMode = splash->getThinLineMode(); - delete splash; - splash = nullptr; - } - if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) { - if (bitmap) { - delete bitmap; - bitmap = nullptr; - } - bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - if (!bitmap->getDataPtr()) { - delete bitmap; - w = h = 1; - bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - } - } - splash = new Splash(bitmap, vectorAntialias, &screenParams); - splash->setThinLineMode(thinLineMode); - splash->setMinLineWidth(s_minLineWidth); - if (state) { - const double *ctm = state->getCTM(); - mat[0] = (SplashCoord)ctm[0]; - mat[1] = (SplashCoord)ctm[1]; - mat[2] = (SplashCoord)ctm[2]; - mat[3] = (SplashCoord)ctm[3]; - mat[4] = (SplashCoord)ctm[4]; - mat[5] = (SplashCoord)ctm[5]; - splash->setMatrix(mat); - } - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - color[0] = 0; - break; - case splashModeXBGR8: - color[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - color[0] = color[1] = color[2] = 0; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - color[0] = color[1] = color[2] = color[3] = 0; - break; - case splashModeDeviceN8: - for (int i = 0; i < 4 + SPOT_NCOMPS; i++) - color[i] = 0; - break; -#endif - } - splash->setStrokePattern(new SplashSolidColor(color)); - splash->setFillPattern(new SplashSolidColor(color)); - splash->setLineCap(splashLineCapButt); - splash->setLineJoin(splashLineJoinMiter); - splash->setLineDash(nullptr, 0, 0); - splash->setMiterLimit(10); - splash->setFlatness(1); - // the SA parameter supposedly defaults to false, but Acrobat - // apparently hardwires it to true - splash->setStrokeAdjust(true); - splash->clear(paperColor, 0); -} - -void SplashOutputDev::endPage() { - if (colorMode != splashModeMono1 && !keepAlphaChannel) { - splash->compositeBackground(paperColor); - } -} - -void SplashOutputDev::saveState(GfxState *state) { - splash->saveState(); - if (t3GlyphStack && !t3GlyphStack->haveDx) { - t3GlyphStack->doNotCache = true; - error(errSyntaxWarning, -1, - "Save (q) operator before d0/d1 in Type 3 glyph"); - } -} - -void SplashOutputDev::restoreState(GfxState *state) { - splash->restoreState(); - needFontUpdate = true; - if (t3GlyphStack && !t3GlyphStack->haveDx) { - t3GlyphStack->doNotCache = true; - error(errSyntaxWarning, -1, - "Restore (Q) operator before d0/d1 in Type 3 glyph"); - } -} - -void SplashOutputDev::updateAll(GfxState *state) { - updateLineDash(state); - updateLineJoin(state); - updateLineCap(state); - updateLineWidth(state); - updateFlatness(state); - updateMiterLimit(state); - updateStrokeAdjust(state); - updateFillColorSpace(state); - updateFillColor(state); - updateStrokeColorSpace(state); - updateStrokeColor(state); - needFontUpdate = true; -} - -void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12, - double m21, double m22, - double m31, double m32) { - SplashCoord mat[6]; - - const double *ctm = state->getCTM(); - mat[0] = (SplashCoord)ctm[0]; - mat[1] = (SplashCoord)ctm[1]; - mat[2] = (SplashCoord)ctm[2]; - mat[3] = (SplashCoord)ctm[3]; - mat[4] = (SplashCoord)ctm[4]; - mat[5] = (SplashCoord)ctm[5]; - splash->setMatrix(mat); -} - -void SplashOutputDev::updateLineDash(GfxState *state) { - double *dashPattern; - int dashLength; - double dashStart; - SplashCoord dash[20]; - int i; - - state->getLineDash(&dashPattern, &dashLength, &dashStart); - if (dashLength > 20) { - dashLength = 20; - } - for (i = 0; i < dashLength; ++i) { - dash[i] = (SplashCoord)dashPattern[i]; - if (dash[i] < 0) { - dash[i] = 0; - } - } - splash->setLineDash(dash, dashLength, (SplashCoord)dashStart); -} - -void SplashOutputDev::updateFlatness(GfxState *state) { -#if 0 // Acrobat ignores the flatness setting, and always renders curves - // with a fairly small flatness value - splash->setFlatness(state->getFlatness()); -#endif -} - -void SplashOutputDev::updateLineJoin(GfxState *state) { - splash->setLineJoin(state->getLineJoin()); -} - -void SplashOutputDev::updateLineCap(GfxState *state) { - splash->setLineCap(state->getLineCap()); -} - -void SplashOutputDev::updateMiterLimit(GfxState *state) { - splash->setMiterLimit(state->getMiterLimit()); -} - -void SplashOutputDev::updateLineWidth(GfxState *state) { - splash->setLineWidth(state->getLineWidth()); -} - -void SplashOutputDev::updateStrokeAdjust(GfxState * /*state*/) { -#if 0 // the SA parameter supposedly defaults to false, but Acrobat - // apparently hardwires it to true - splash->setStrokeAdjust(state->getStrokeAdjust()); -#endif -} - -void SplashOutputDev::updateFillColorSpace(GfxState *state) { -#ifdef SPLASH_CMYK - if (colorMode == splashModeDeviceN8) - state->getFillColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif -} - -void SplashOutputDev::updateStrokeColorSpace(GfxState *state) { -#ifdef SPLASH_CMYK - if (colorMode == splashModeDeviceN8) - state->getStrokeColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif -} - -void SplashOutputDev::updateFillColor(GfxState *state) { - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - state->getFillGray(&gray); - splash->setFillPattern(getColor(gray)); - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - state->getFillRGB(&rgb); - splash->setFillPattern(getColor(&rgb)); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - state->getFillCMYK(&cmyk); - splash->setFillPattern(getColor(&cmyk)); - break; - case splashModeDeviceN8: - state->getFillDeviceN(&deviceN); - splash->setFillPattern(getColor(&deviceN)); - break; -#endif - } -} - -void SplashOutputDev::updateStrokeColor(GfxState *state) { - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - state->getStrokeGray(&gray); - splash->setStrokePattern(getColor(gray)); - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - state->getStrokeRGB(&rgb); - splash->setStrokePattern(getColor(&rgb)); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - state->getStrokeCMYK(&cmyk); - splash->setStrokePattern(getColor(&cmyk)); - break; - case splashModeDeviceN8: - state->getStrokeDeviceN(&deviceN); - splash->setStrokePattern(getColor(&deviceN)); - break; -#endif - } -} - -SplashPattern *SplashOutputDev::getColor(GfxGray gray) { - SplashColor color; - - if (reverseVideo) { - gray = gfxColorComp1 - gray; - } - color[0] = colToByte(gray); - return new SplashSolidColor(color); -} - -SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) { - GfxColorComp r, g, b; - SplashColor color; - - if (reverseVideo) { - r = gfxColorComp1 - rgb->r; - g = gfxColorComp1 - rgb->g; - b = gfxColorComp1 - rgb->b; - } else { - r = rgb->r; - g = rgb->g; - b = rgb->b; - } - color[0] = colToByte(r); - color[1] = colToByte(g); - color[2] = colToByte(b); - if (colorMode == splashModeXBGR8) color[3] = 255; - return new SplashSolidColor(color); -} - -#ifdef SPLASH_CMYK -SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) { - SplashColor color; - - color[0] = colToByte(cmyk->c); - color[1] = colToByte(cmyk->m); - color[2] = colToByte(cmyk->y); - color[3] = colToByte(cmyk->k); - return new SplashSolidColor(color); -} - -SplashPattern *SplashOutputDev::getColor(GfxColor *deviceN) { - SplashColor color; - - for (int i = 0; i < 4 + SPOT_NCOMPS; i++) - color[i] = colToByte(deviceN->c[i]); - return new SplashSolidColor(color); -} -#endif - -void SplashOutputDev::getMatteColor(SplashColorMode colorMode, GfxImageColorMap *colorMap, const GfxColor *matteColorIn, SplashColor matteColor) { - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - colorMap->getColorSpace()->getGray(matteColorIn, &gray); - matteColor[0] = colToByte(gray); - break; - case splashModeRGB8: - case splashModeBGR8: - colorMap->getColorSpace()->getRGB(matteColorIn, &rgb); - matteColor[0] = colToByte(rgb.r); - matteColor[1] = colToByte(rgb.g); - matteColor[2] = colToByte(rgb.b); - break; - case splashModeXBGR8: - colorMap->getColorSpace()->getRGB(matteColorIn, &rgb); - matteColor[0] = colToByte(rgb.r); - matteColor[1] = colToByte(rgb.g); - matteColor[2] = colToByte(rgb.b); - matteColor[3] = 255; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - colorMap->getColorSpace()->getCMYK(matteColorIn, &cmyk); - matteColor[0] = colToByte(cmyk.c); - matteColor[1] = colToByte(cmyk.m); - matteColor[2] = colToByte(cmyk.y); - matteColor[3] = colToByte(cmyk.k); - break; - case splashModeDeviceN8: - colorMap->getColorSpace()->getDeviceN(matteColorIn, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - matteColor[cp] = colToByte(deviceN.c[cp]); - break; -#endif - } -} - -void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace, - bool overprintFlag, - int overprintMode, - const GfxColor *singleColor, - bool grayIndexed) { -#ifdef SPLASH_CMYK - unsigned int mask; - GfxCMYK cmyk; - bool additive = false; - int i; - - if (colorSpace->getMode() == csIndexed) { - setOverprintMask(((GfxIndexedColorSpace *)colorSpace)->getBase(), - overprintFlag, - overprintMode, - singleColor, - grayIndexed); - return; - } - if (overprintFlag && overprintPreview) { - mask = colorSpace->getOverprintMask(); - if (singleColor && overprintMode && - colorSpace->getMode() == csDeviceCMYK) { - colorSpace->getCMYK(singleColor, &cmyk); - if (cmyk.c == 0) { - mask &= ~1; - } - if (cmyk.m == 0) { - mask &= ~2; - } - if (cmyk.y == 0) { - mask &= ~4; - } - if (cmyk.k == 0) { - mask &= ~8; - } - } - if (grayIndexed) { - mask &= ~7; - } else if (colorSpace->getMode() == csSeparation) { - GfxSeparationColorSpace *deviceSep = (GfxSeparationColorSpace *)colorSpace; - additive = deviceSep->getName()->cmp("All") != 0 && mask == 0x0f && !deviceSep->isNonMarking(); - } else if (colorSpace->getMode() == csDeviceN) { - GfxDeviceNColorSpace *deviceNCS = (GfxDeviceNColorSpace *)colorSpace; - additive = mask == 0x0f && !deviceNCS->isNonMarking(); - for (i = 0; i < deviceNCS->getNComps() && additive; i++) { - if (deviceNCS->getColorantName(i)->cmp("Cyan") == 0) { - additive = false; - } else if (deviceNCS->getColorantName(i)->cmp("Magenta") == 0) { - additive = false; - } else if (deviceNCS->getColorantName(i)->cmp("Yellow") == 0) { - additive = false; - } else if (deviceNCS->getColorantName(i)->cmp("Black") == 0) { - additive = false; - } - } - } - } else { - mask = 0xffffffff; - } - splash->setOverprintMask(mask, additive); -#endif -} - -void SplashOutputDev::updateBlendMode(GfxState *state) { - splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]); -} - -void SplashOutputDev::updateFillOpacity(GfxState *state) { - splash->setFillAlpha((SplashCoord)state->getFillOpacity()); - if (transpGroupStack != nullptr && (SplashCoord)state->getFillOpacity() < transpGroupStack->knockoutOpacity) { - transpGroupStack->knockoutOpacity = (SplashCoord)state->getFillOpacity(); - } -} - -void SplashOutputDev::updateStrokeOpacity(GfxState *state) { - splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity()); - if (transpGroupStack != nullptr && (SplashCoord)state->getStrokeOpacity() < transpGroupStack->knockoutOpacity) { - transpGroupStack->knockoutOpacity = (SplashCoord)state->getStrokeOpacity(); - } -} - -void SplashOutputDev::updatePatternOpacity(GfxState *state) { - splash->setPatternAlpha((SplashCoord)state->getStrokeOpacity(), (SplashCoord)state->getFillOpacity()); -} - -void SplashOutputDev::clearPatternOpacity(GfxState *state) { - splash->clearPatternAlpha(); -} - -void SplashOutputDev::updateFillOverprint(GfxState *state) { - splash->setFillOverprint(state->getFillOverprint()); -} - -void SplashOutputDev::updateStrokeOverprint(GfxState *state) { - splash->setStrokeOverprint(state->getStrokeOverprint()); -} - -void SplashOutputDev::updateOverprintMode(GfxState *state) { - splash->setOverprintMode(state->getOverprintMode()); -} - -void SplashOutputDev::updateTransfer(GfxState *state) { - Function **transfer; - unsigned char red[256], green[256], blue[256], gray[256]; - double x, y; - int i; - - transfer = state->getTransfer(); - if (transfer[0] && - transfer[0]->getInputSize() == 1 && - transfer[0]->getOutputSize() == 1) { - if (transfer[1] && - transfer[1]->getInputSize() == 1 && - transfer[1]->getOutputSize() == 1 && - transfer[2] && - transfer[2]->getInputSize() == 1 && - transfer[2]->getOutputSize() == 1 && - transfer[3] && - transfer[3]->getInputSize() == 1 && - transfer[3]->getOutputSize() == 1) { - for (i = 0; i < 256; ++i) { - x = i / 255.0; - transfer[0]->transform(&x, &y); - red[i] = (unsigned char)(y * 255.0 + 0.5); - transfer[1]->transform(&x, &y); - green[i] = (unsigned char)(y * 255.0 + 0.5); - transfer[2]->transform(&x, &y); - blue[i] = (unsigned char)(y * 255.0 + 0.5); - transfer[3]->transform(&x, &y); - gray[i] = (unsigned char)(y * 255.0 + 0.5); - } - } else { - for (i = 0; i < 256; ++i) { - x = i / 255.0; - transfer[0]->transform(&x, &y); - red[i] = green[i] = blue[i] = gray[i] = (unsigned char)(y * 255.0 + 0.5); - } - } - } else { - for (i = 0; i < 256; ++i) { - red[i] = green[i] = blue[i] = gray[i] = (unsigned char)i; - } - } - splash->setTransfer(red, green, blue, gray); -} - -void SplashOutputDev::updateFont(GfxState * /*state*/) { - needFontUpdate = true; -} - -void SplashOutputDev::doUpdateFont(GfxState *state) { - GfxFont *gfxFont; - GfxFontLoc *fontLoc; - GfxFontType fontType; - SplashOutFontFileID *id = nullptr; - SplashFontFile *fontFile; - SplashFontSrc *fontsrc = nullptr; - FoFiTrueType *ff; - GooString *fileName; - char *tmpBuf; - int tmpBufLen; - int *codeToGID; - const double *textMat; - double m11, m12, m21, m22, fontSize; - int faceIndex = 0; - SplashCoord mat[4]; - int n, i; - bool recreateFont = false; - bool doAdjustFontMatrix = false; - - needFontUpdate = false; - font = nullptr; - fileName = nullptr; - tmpBuf = nullptr; - fontLoc = nullptr; - - if (!(gfxFont = state->getFont())) { - goto err1; - } - fontType = gfxFont->getType(); - if (fontType == fontType3) { - goto err1; - } - - // sanity-check the font size - skip anything larger than 10 inches - // (this avoids problems allocating memory for the font cache) - if (state->getTransformedFontSize() - > 10 * (state->getHDPI() + state->getVDPI())) { - goto err1; - } - - // check the font file cache -reload: - delete id; - delete fontLoc; - fontLoc = nullptr; - if (fontsrc && !fontsrc->isFile) { - fontsrc->unref(); - fontsrc = nullptr; - } - - id = new SplashOutFontFileID(gfxFont->getID()); - if ((fontFile = fontEngine->getFontFile(id))) { - delete id; - - } else { - - if (!(fontLoc = gfxFont->locateFont((xref) ? xref : doc->getXRef(), nullptr))) { - error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->c_str() - : "(unnamed)"); - goto err2; - } - - // embedded font - if (fontLoc->locType == gfxFontLocEmbedded) { - // if there is an embedded font, read it to memory - tmpBuf = gfxFont->readEmbFontFile((xref) ? xref : doc->getXRef(), &tmpBufLen); - if (! tmpBuf) - goto err2; - - // external font - } else { // gfxFontLocExternal - fileName = fontLoc->path; - fontType = fontLoc->fontType; - doAdjustFontMatrix = true; - } - - fontsrc = new SplashFontSrc; - if (fileName) - fontsrc->setFile(fileName, false); - else - fontsrc->setBuf(tmpBuf, tmpBufLen, true); - - // load the font file - switch (fontType) { - case fontType1: - if (!(fontFile = fontEngine->loadType1Font( - id, - fontsrc, - (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->c_str() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontType1C: - if (!(fontFile = fontEngine->loadType1CFont( - id, - fontsrc, - (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->c_str() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontType1COT: - if (!(fontFile = fontEngine->loadOpenTypeT1CFont( - id, - fontsrc, - (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->c_str() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontTrueType: - case fontTrueTypeOT: - if (fileName) - ff = FoFiTrueType::load(fileName->c_str()); - else - ff = FoFiTrueType::make(tmpBuf, tmpBufLen); - if (ff) { - codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); - n = 256; - delete ff; - // if we're substituting for a non-TrueType font, we need to mark - // all notdef codes as "do not draw" (rather than drawing TrueType - // notdef glyphs) - if (gfxFont->getType() != fontTrueType && - gfxFont->getType() != fontTrueTypeOT) { - for (i = 0; i < 256; ++i) { - if (codeToGID[i] == 0) { - codeToGID[i] = -1; - } - } - } - } else { - codeToGID = nullptr; - n = 0; - } - if (!(fontFile = fontEngine->loadTrueTypeFont( - id, - fontsrc, - codeToGID, n))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->c_str() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontCIDType0: - case fontCIDType0C: - if (!(fontFile = fontEngine->loadCIDFont( - id, - fontsrc))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->c_str() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontCIDType0COT: - if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { - n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); - codeToGID = (int *)gmallocn(n, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), - n * sizeof(int)); - } else { - codeToGID = nullptr; - n = 0; - } - if (!(fontFile = fontEngine->loadOpenTypeCFFFont( - id, - fontsrc, - codeToGID, n))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->c_str() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - case fontCIDType2: - case fontCIDType2OT: - codeToGID = nullptr; - n = 0; - if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { - n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); - if (n) { - codeToGID = (int *)gmallocn(n, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), - n * sizeof(int)); - } - } else { - if (fileName) - ff = FoFiTrueType::load(fileName->c_str()); - else - ff = FoFiTrueType::make(tmpBuf, tmpBufLen); - if (! ff) - { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->c_str() - : "(unnamed)"); - goto err2; - } - codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n); - delete ff; - } - if (!(fontFile = fontEngine->loadTrueTypeFont( - id, - fontsrc, - codeToGID, n, faceIndex))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->c_str() - : "(unnamed)"); - if (gfxFont->invalidateEmbeddedFont()) goto reload; - goto err2; - } - break; - default: - // this shouldn't happen - goto err2; - } - fontFile->doAdjustMatrix = doAdjustFontMatrix; - } - - // get the font matrix - textMat = state->getTextMat(); - fontSize = state->getFontSize(); - m11 = textMat[0] * fontSize * state->getHorizScaling(); - m12 = textMat[1] * fontSize * state->getHorizScaling(); - m21 = textMat[2] * fontSize; - m22 = textMat[3] * fontSize; - - // create the scaled font - mat[0] = m11; mat[1] = m12; - mat[2] = m21; mat[3] = m22; - font = fontEngine->getFont(fontFile, mat, splash->getMatrix()); - - // for substituted fonts: adjust the font matrix -- compare the - // width of 'm' in the original font and the substituted font - if (fontFile->doAdjustMatrix && !gfxFont->isCIDFont()) { - double w1, w2, w3; - CharCode code; - const char *name; - for (code = 0; code < 256; ++code) { - if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && - name[0] == 'm' && name[1] == '\0') { - break; - } - } - if (code < 256) { - w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code); - w2 = font->getGlyphAdvance(code); - w3 = ((Gfx8BitFont *)gfxFont)->getWidth(0); - if (!gfxFont->isSymbolic() && w2 > 0 && w1 > w3) { - // if real font is substantially narrower than substituted - // font, reduce the font size accordingly - if (w1 > 0.01 && w1 < 0.9 * w2) { - w1 /= w2; - m11 *= w1; - m21 *= w1; - recreateFont = true; - } - } - } - } - - if (recreateFont) - { - mat[0] = m11; mat[1] = m12; - mat[2] = m21; mat[3] = m22; - font = fontEngine->getFont(fontFile, mat, splash->getMatrix()); - } - - delete fontLoc; - if (fontsrc && !fontsrc->isFile) - fontsrc->unref(); - return; - - err2: - delete id; - delete fontLoc; - err1: - if (fontsrc && !fontsrc->isFile) - fontsrc->unref(); - return; -} - -void SplashOutputDev::stroke(GfxState *state) { - if (state->getStrokeColorSpace()->isNonMarking()) { - return; - } - setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), - state->getOverprintMode(), state->getStrokeColor()); - SplashPath path = convertPath(state, state->getPath(), false); - splash->stroke(&path); -} - -void SplashOutputDev::fill(GfxState *state) { - if (state->getFillColorSpace()->isNonMarking()) { - return; - } - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - SplashPath path = convertPath(state, state->getPath(), true); - splash->fill(&path, false); -} - -void SplashOutputDev::eoFill(GfxState *state) { - if (state->getFillColorSpace()->isNonMarking()) { - return; - } - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - SplashPath path = convertPath(state, state->getPath(), true); - splash->fill(&path, true); -} - -void SplashOutputDev::clip(GfxState *state) { - SplashPath path = convertPath(state, state->getPath(), true); - splash->clipToPath(&path, false); -} - -void SplashOutputDev::eoClip(GfxState *state) { - SplashPath path = convertPath(state, state->getPath(), true); - splash->clipToPath(&path, true); -} - -void SplashOutputDev::clipToStrokePath(GfxState *state) { - SplashPath *path2; - - SplashPath path = convertPath(state, state->getPath(), false); - path2 = splash->makeStrokePath(&path, state->getLineWidth()); - splash->clipToPath(path2, false); - delete path2; -} - -SplashPath SplashOutputDev::convertPath(GfxState *state, GfxPath *path, - bool dropEmptySubpaths) { - SplashPath sPath; - GfxSubpath *subpath; - int n, i, j; - - n = dropEmptySubpaths ? 1 : 0; - for (i = 0; i < path->getNumSubpaths(); ++i) { - subpath = path->getSubpath(i); - if (subpath->getNumPoints() > n) { - sPath.reserve(subpath->getNumPoints() + 1); - sPath.moveTo((SplashCoord)subpath->getX(0), - (SplashCoord)subpath->getY(0)); - j = 1; - while (j < subpath->getNumPoints()) { - if (subpath->getCurve(j)) { - sPath.curveTo((SplashCoord)subpath->getX(j), - (SplashCoord)subpath->getY(j), - (SplashCoord)subpath->getX(j+1), - (SplashCoord)subpath->getY(j+1), - (SplashCoord)subpath->getX(j+2), - (SplashCoord)subpath->getY(j+2)); - j += 3; - } else { - sPath.lineTo((SplashCoord)subpath->getX(j), - (SplashCoord)subpath->getY(j)); - ++j; - } - } - if (subpath->isClosed()) { - sPath.close(); - } - } - } - return sPath; -} - -void SplashOutputDev::drawChar(GfxState *state, double x, double y, - double dx, double dy, - double originX, double originY, - CharCode code, int nBytes, - Unicode *u, int uLen) { - SplashPath *path; - int render; - bool doFill, doStroke, doClip, strokeAdjust; - double m[4]; - bool horiz; - - if (skipHorizText || skipRotatedText) { - state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); - horiz = m[0] > 0 && fabs(m[1]) < 0.001 && - fabs(m[2]) < 0.001 && m[3] < 0; - if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { - return; - } - } - - // check for invisible text -- this is used by Acrobat Capture - render = state->getRender(); - if (render == 3) { - return; - } - - if (needFontUpdate) { - doUpdateFont(state); - } - if (!font) { - return; - } - - x -= originX; - y -= originY; - - doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking(); - doStroke = ((render & 3) == 1 || (render & 3) == 2) && - !state->getStrokeColorSpace()->isNonMarking(); - doClip = render & 4; - - path = nullptr; - SplashCoord lineWidth = splash->getLineWidth(); - if (doStroke && lineWidth == 0.0) - splash->setLineWidth(1 / state->getVDPI()); - if (doStroke || doClip) { - if ((path = font->getGlyphPath(code))) { - path->offset((SplashCoord)x, (SplashCoord)y); - } - } - - // don't use stroke adjustment when stroking text -- the results - // tend to be ugly (because characters with horizontal upper or - // lower edges get misaligned relative to the other characters) - strokeAdjust = false; // make gcc happy - if (doStroke) { - strokeAdjust = splash->getStrokeAdjust(); - splash->setStrokeAdjust(false); - } - - // fill and stroke - if (doFill && doStroke) { - if (path) { - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - splash->fill(path, false); - setOverprintMask(state->getStrokeColorSpace(), - state->getStrokeOverprint(), - state->getOverprintMode(), - state->getStrokeColor()); - splash->stroke(path); - } - - // fill - } else if (doFill) { - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); - - // stroke - } else if (doStroke) { - if (path) { - setOverprintMask(state->getStrokeColorSpace(), - state->getStrokeOverprint(), - state->getOverprintMode(), - state->getStrokeColor()); - splash->stroke(path); - } - } - splash->setLineWidth(lineWidth); - - // clip - if (doClip) { - if (path) { - if (textClipPath) { - textClipPath->append(path); - } else { - textClipPath = path; - path = nullptr; - } - } - } - - if (doStroke) { - splash->setStrokeAdjust(strokeAdjust); - } - - if (path) { - delete path; - } -} - -bool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, - double dx, double dy, - CharCode code, Unicode *u, int uLen) { - GfxFont *gfxFont; - const Ref *fontID; - const double *ctm, *bbox; - T3FontCache *t3Font; - T3GlyphStack *t3gs; - bool validBBox; - double m[4]; - bool horiz; - double x1, y1, xMin, yMin, xMax, yMax, xt, yt; - int i, j; - - // check for invisible text -- this is used by Acrobat Capture - if (state->getRender() == 3) { - // this is a bit of cheating, we say yes, font is already on cache - // so we actually skip the rendering of it - return true; - } - - if (skipHorizText || skipRotatedText) { - state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); - horiz = m[0] > 0 && fabs(m[1]) < 0.001 && - fabs(m[2]) < 0.001 && m[3] < 0; - if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { - return true; - } - } - - if (!(gfxFont = state->getFont())) { - return false; - } - fontID = gfxFont->getID(); - ctm = state->getCTM(); - state->transform(0, 0, &xt, &yt); - - // is it the first (MRU) font in the cache? - if (!(nT3Fonts > 0 && - t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) { - - // is the font elsewhere in the cache? - for (i = 1; i < nT3Fonts; ++i) { - if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) { - t3Font = t3FontCache[i]; - for (j = i; j > 0; --j) { - t3FontCache[j] = t3FontCache[j - 1]; - } - t3FontCache[0] = t3Font; - break; - } - } - if (i >= nT3Fonts) { - - // create new entry in the font cache - if (nT3Fonts == splashOutT3FontCacheSize) { - t3gs = t3GlyphStack; - while (t3gs != nullptr) { - if (t3gs->cache == t3FontCache[nT3Fonts - 1]) { - error(errSyntaxWarning, -1, "t3FontCache reaches limit but font still on stack in SplashOutputDev::beginType3Char"); - return true; - } - t3gs = t3gs->next; - } - delete t3FontCache[nT3Fonts - 1]; - --nT3Fonts; - } - for (j = nT3Fonts; j > 0; --j) { - t3FontCache[j] = t3FontCache[j - 1]; - } - ++nT3Fonts; - bbox = gfxFont->getFontBBox(); - if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) { - // unspecified bounding box -- just take a guess - xMin = xt - 5; - xMax = xMin + 30; - yMax = yt + 15; - yMin = yMax - 45; - validBBox = false; - } else { - state->transform(bbox[0], bbox[1], &x1, &y1); - xMin = xMax = x1; - yMin = yMax = y1; - state->transform(bbox[0], bbox[3], &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - state->transform(bbox[2], bbox[1], &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - state->transform(bbox[2], bbox[3], &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - validBBox = true; - } - t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3], - (int)floor(xMin - xt) - 2, - (int)floor(yMin - yt) - 2, - (int)ceil(xMax) - (int)floor(xMin) + 4, - (int)ceil(yMax) - (int)floor(yMin) + 4, - validBBox, - colorMode != splashModeMono1); - } - } - t3Font = t3FontCache[0]; - - // is the glyph in the cache? - i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; - for (j = 0; j < t3Font->cacheAssoc; ++j) { - if (t3Font->cacheTags != nullptr) { - if ((t3Font->cacheTags[i+j].mru & 0x8000) && - t3Font->cacheTags[i+j].code == code) { - drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j], - t3Font->cacheData + (i+j) * t3Font->glyphSize); - return true; - } - } - } - - // push a new Type 3 glyph record - t3gs = new T3GlyphStack(); - t3gs->next = t3GlyphStack; - t3GlyphStack = t3gs; - t3GlyphStack->code = code; - t3GlyphStack->cache = t3Font; - t3GlyphStack->cacheTag = nullptr; - t3GlyphStack->cacheData = nullptr; - t3GlyphStack->haveDx = false; - t3GlyphStack->doNotCache = false; - - return false; -} - -void SplashOutputDev::endType3Char(GfxState *state) { - T3GlyphStack *t3gs; - - if (t3GlyphStack->cacheTag) { - --nestCount; - memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(), - t3GlyphStack->cache->glyphSize); - delete bitmap; - delete splash; - bitmap = t3GlyphStack->origBitmap; - splash = t3GlyphStack->origSplash; - const double *ctm = state->getCTM(); - state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], - t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); - updateCTM(state, 0, 0, 0, 0, 0, 0); - drawType3Glyph(state, t3GlyphStack->cache, - t3GlyphStack->cacheTag, t3GlyphStack->cacheData); - } - t3gs = t3GlyphStack; - t3GlyphStack = t3gs->next; - delete t3gs; -} - -void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) { - if (likely(t3GlyphStack != nullptr)) { - t3GlyphStack->haveDx = true; - } else { - error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D0"); - } -} - -void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, - double llx, double lly, double urx, double ury) { - T3FontCache *t3Font; - SplashColor color; - double xt, yt, xMin, xMax, yMin, yMax, x1, y1; - int i, j; - - // ignore multiple d0/d1 operators - if (!t3GlyphStack || t3GlyphStack->haveDx) { - return; - } - t3GlyphStack->haveDx = true; - // don't cache if we got a gsave/grestore before the d1 - if (t3GlyphStack->doNotCache) { - return; - } - - if (unlikely(t3GlyphStack == nullptr)) { - error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D1"); - return; - } - - if (unlikely(t3GlyphStack->origBitmap != nullptr)) { - error(errSyntaxWarning, -1, "t3GlyphStack origBitmap was not null in SplashOutputDev::type3D1"); - return; - } - - if (unlikely(t3GlyphStack->origSplash != nullptr)) { - error(errSyntaxWarning, -1, "t3GlyphStack origSplash was not null in SplashOutputDev::type3D1"); - return; - } - - t3Font = t3GlyphStack->cache; - - // check for a valid bbox - state->transform(0, 0, &xt, &yt); - state->transform(llx, lly, &x1, &y1); - xMin = xMax = x1; - yMin = yMax = y1; - state->transform(llx, ury, &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - state->transform(urx, lly, &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - state->transform(urx, ury, &x1, &y1); - if (x1 < xMin) { - xMin = x1; - } else if (x1 > xMax) { - xMax = x1; - } - if (y1 < yMin) { - yMin = y1; - } else if (y1 > yMax) { - yMax = y1; - } - if (xMin - xt < t3Font->glyphX || - yMin - yt < t3Font->glyphY || - xMax - xt > t3Font->glyphX + t3Font->glyphW || - yMax - yt > t3Font->glyphY + t3Font->glyphH) { - if (t3Font->validBBox) { - error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph"); - } - return; - } - - if (t3Font->cacheTags == nullptr) - return; - - // allocate a cache entry - i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; - for (j = 0; j < t3Font->cacheAssoc; ++j) { - if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) { - t3Font->cacheTags[i+j].mru = 0x8000; - t3Font->cacheTags[i+j].code = t3GlyphStack->code; - t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j]; - t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize; - } else { - ++t3Font->cacheTags[i+j].mru; - } - } - - // save state - t3GlyphStack->origBitmap = bitmap; - t3GlyphStack->origSplash = splash; - const double *ctm = state->getCTM(); - t3GlyphStack->origCTM4 = ctm[4]; - t3GlyphStack->origCTM5 = ctm[5]; - - // create the temporary bitmap - if (colorMode == splashModeMono1) { - bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, - splashModeMono1, false); - splash = new Splash(bitmap, false, - t3GlyphStack->origSplash->getScreen()); - color[0] = 0; - splash->clear(color); - color[0] = 0xff; - } else { - bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, - splashModeMono8, false); - splash = new Splash(bitmap, vectorAntialias, - t3GlyphStack->origSplash->getScreen()); - color[0] = 0x00; - splash->clear(color); - color[0] = 0xff; - } - splash->setMinLineWidth(s_minLineWidth); - splash->setThinLineMode(splashThinLineDefault); - splash->setFillPattern(new SplashSolidColor(color)); - splash->setStrokePattern(new SplashSolidColor(color)); - //~ this should copy other state from t3GlyphStack->origSplash? - state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], - -t3Font->glyphX, -t3Font->glyphY); - updateCTM(state, 0, 0, 0, 0, 0, 0); - ++nestCount; -} - -void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font, - T3FontCacheTag * /*tag*/, unsigned char *data) { - SplashGlyphBitmap glyph; - - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - glyph.x = -t3Font->glyphX; - glyph.y = -t3Font->glyphY; - glyph.w = t3Font->glyphW; - glyph.h = t3Font->glyphH; - glyph.aa = colorMode != splashModeMono1; - glyph.data = data; - glyph.freeData = false; - splash->fillGlyph(0, 0, &glyph); -} - -void SplashOutputDev::beginTextObject(GfxState *state) { -} - -void SplashOutputDev::endTextObject(GfxState *state) { - if (textClipPath) { - splash->clipToPath(textClipPath, false); - delete textClipPath; - textClipPath = nullptr; - } -} - -struct SplashOutImageMaskData { - ImageStream *imgStr; - bool invert; - int width, height, y; -}; - -bool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) { - SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data; - unsigned char *p; - SplashColorPtr q; - int x; - - if (imgMaskData->y == imgMaskData->height) { - return false; - } - if (!(p = imgMaskData->imgStr->getLine())) { - return false; - } - for (x = 0, q = line; x < imgMaskData->width; ++x) { - *q++ = *p++ ^ imgMaskData->invert; - } - ++imgMaskData->y; - return true; -} - -void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, bool invert, - bool interpolate, bool inlineImg) { - SplashCoord mat[6]; - SplashOutImageMaskData imgMaskData; - - if (state->getFillColorSpace()->isNonMarking()) { - return; - } - setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), state->getFillColor()); - - const double *ctm = state->getCTM(); - for (int i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) return; - } - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - - imgMaskData.imgStr = new ImageStream(str, width, 1, 1); - imgMaskData.imgStr->reset(); - imgMaskData.invert = invert ? 0 : 1; - imgMaskData.width = width; - imgMaskData.height = height; - imgMaskData.y = 0; - - splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != nullptr); - if (inlineImg) { - while (imgMaskData.y < height) { - imgMaskData.imgStr->getLine(); - ++imgMaskData.y; - } - } - - delete imgMaskData.imgStr; - str->close(); -} - -void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, - Object *ref, Stream *str, - int width, int height, - bool invert, - bool inlineImg, double *baseMatrix) { - const double *ctm; - SplashCoord mat[6]; - SplashOutImageMaskData imgMaskData; - Splash *maskSplash; - SplashColor maskColor; - double bbox[4] = {0, 0, 1, 1}; // default; - - if (state->getFillColorSpace()->isNonMarking()) { - return; - } - - ctm = state->getCTM(); - for (int i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) return; - } - - beginTransparencyGroup(state, bbox, nullptr, false, false, false); - baseMatrix[4] -= transpGroupStack->tx; - baseMatrix[5] -= transpGroupStack->ty; - - ctm = state->getCTM(); - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - imgMaskData.imgStr = new ImageStream(str, width, 1, 1); - imgMaskData.imgStr->reset(); - imgMaskData.invert = invert ? 0 : 1; - imgMaskData.width = width; - imgMaskData.height = height; - imgMaskData.y = 0; - - transpGroupStack->softmask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, false); - maskSplash = new Splash(transpGroupStack->softmask, vectorAntialias); - maskColor[0] = 0; - maskSplash->clear(maskColor); - maskColor[0] = 0xff; - maskSplash->setFillPattern(new SplashSolidColor(maskColor)); - maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != nullptr); - delete maskSplash; - delete imgMaskData.imgStr; - str->close(); -} - -void SplashOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { - double bbox[4] = {0,0,1,1}; // dummy - - /* transfer mask to alpha channel! */ - // memcpy(maskBitmap->getAlphaPtr(), maskBitmap->getDataPtr(), bitmap->getRowSize() * bitmap->getHeight()); - // memset(maskBitmap->getDataPtr(), 0, bitmap->getRowSize() * bitmap->getHeight()); - if (transpGroupStack->softmask != nullptr) { - unsigned char *dest = bitmap->getAlphaPtr(); - unsigned char *src = transpGroupStack->softmask->getDataPtr(); - for (int c= 0; c < transpGroupStack->softmask->getRowSize() * transpGroupStack->softmask->getHeight(); c++) { - dest[c] = src[c]; - } - delete transpGroupStack->softmask; - transpGroupStack->softmask = nullptr; - } - endTransparencyGroup(state); - baseMatrix[4] += transpGroupStack->tx; - baseMatrix[5] += transpGroupStack->ty; - paintTransparencyGroup(state, bbox); -} - -struct SplashOutImageData { - ImageStream *imgStr; - GfxImageColorMap *colorMap; - SplashColorPtr lookup; - int *maskColors; - SplashColorMode colorMode; - int width, height, y; - ImageStream *maskStr; - GfxImageColorMap *maskColorMap; - SplashColor matteColor; -}; - -#ifdef USE_CMS -bool SplashOutputDev::useIccImageSrc(void *data) { - SplashOutImageData *imgData = (SplashOutImageData *)data; - - if (!imgData->lookup && imgData->colorMap->getColorSpace()->getMode() == csICCBased) { - GfxICCBasedColorSpace *colorSpace = (GfxICCBasedColorSpace *) imgData->colorMap->getColorSpace(); - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceGray) - return true; - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceRGB) - return true; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceCMYK) - return true; - break; - case splashModeDeviceN8: - if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceN) - return true; - break; -#endif - } - } - - return false; -} -#endif - -// Clip x to lie in [0, 255]. -static inline unsigned char clip255(int x) { - return x < 0 ? 0 : x > 255 ? 255 : x; -} - -bool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, - unsigned char * /*alphaLine*/) { - SplashOutImageData *imgData = (SplashOutImageData *)data; - unsigned char *p; - SplashColorPtr q, col; - GfxRGB rgb; - GfxGray gray; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - int nComps, x; - - if (imgData->y == imgData->height) { - return false; - } - if (!(p = imgData->imgStr->getLine())) { - int destComps = 1; - if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8) - destComps = 3; - else if (imgData->colorMode == splashModeXBGR8) - destComps = 4; -#ifdef SPLASH_CMYK - else if (imgData->colorMode == splashModeCMYK8) - destComps = 4; - else if (imgData->colorMode == splashModeDeviceN8) - destComps = SPOT_NCOMPS + 4; -#endif - memset(colorLine, 0, imgData->width * destComps); - return false; - } - - nComps = imgData->colorMap->getNumPixelComps(); - - if (imgData->lookup) { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - *q++ = imgData->lookup[*p]; - } - break; - case splashModeRGB8: - case splashModeBGR8: - for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - col = &imgData->lookup[3 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - } - break; - case splashModeXBGR8: - for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = col[3]; - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = col[3]; - } - break; - case splashModeDeviceN8: - for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - col = &imgData->lookup[(SPOT_NCOMPS+4) * *p]; - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = col[cp]; - } - break; -#endif - } - } else { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getGray(p, &gray); - *q++ = colToByte(gray); - } - break; - case splashModeRGB8: - case splashModeBGR8: - if (imgData->colorMap->useRGBLine()) { - imgData->colorMap->getRGBLine(p, (unsigned char *) colorLine, imgData->width); - } else { - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getRGB(p, &rgb); - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); - *q++ = colToByte(rgb.b); - } - } - break; - case splashModeXBGR8: - if (imgData->colorMap->useRGBLine()) { - imgData->colorMap->getRGBXLine(p, (unsigned char *) colorLine, imgData->width); - } else { - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getRGB(p, &rgb); - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); - *q++ = colToByte(rgb.b); - *q++ = 255; - } - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - if (imgData->colorMap->useCMYKLine()) { - imgData->colorMap->getCMYKLine(p, (unsigned char *) colorLine, imgData->width); - } else { - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getCMYK(p, &cmyk); - *q++ = colToByte(cmyk.c); - *q++ = colToByte(cmyk.m); - *q++ = colToByte(cmyk.y); - *q++ = colToByte(cmyk.k); - } - } - break; - case splashModeDeviceN8: - if (imgData->colorMap->useDeviceNLine()) { - imgData->colorMap->getDeviceNLine(p, (unsigned char *) colorLine, imgData->width); - } else { - for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getDeviceN(p, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = colToByte(deviceN.c[cp]); - } - } - break; -#endif - } - } - - if (imgData->maskStr != nullptr && (p = imgData->maskStr->getLine()) != nullptr) { - int destComps = splashColorModeNComps[imgData->colorMode]; - int convComps = (imgData->colorMode == splashModeXBGR8) ? 3 : destComps; - imgData->maskColorMap->getGrayLine(p, p, imgData->width); - for (x = 0, q = colorLine; x < imgData->width; ++x, p++, q += destComps) { - for (int cp = 0; cp < convComps; cp++) { - q[cp] = (*p) ? clip255(imgData->matteColor[cp] + (int) (q[cp] - imgData->matteColor[cp]) * 255 / *p) : imgData->matteColor[cp]; - } - } - } - ++imgData->y; - return true; -} - -#ifdef USE_CMS -bool SplashOutputDev::iccImageSrc(void *data, SplashColorPtr colorLine, - unsigned char * /*alphaLine*/) { - SplashOutImageData *imgData = (SplashOutImageData *)data; - unsigned char *p; - int nComps; - - if (imgData->y == imgData->height) { - return false; - } - if (!(p = imgData->imgStr->getLine())) { - int destComps = 1; - if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8) - destComps = 3; - else if (imgData->colorMode == splashModeXBGR8) - destComps = 4; -#ifdef SPLASH_CMYK - else if (imgData->colorMode == splashModeCMYK8) - destComps = 4; - else if (imgData->colorMode == splashModeDeviceN8) - destComps = SPOT_NCOMPS + 4; -#endif - memset(colorLine, 0, imgData->width * destComps); - return false; - } - - if (imgData->colorMode == splashModeXBGR8) { - SplashColorPtr q; - int x; - for (x = 0, q = colorLine; x < imgData->width; ++x) { - *q++ = *p++; - *q++ = *p++; - *q++ = *p++; - *q++ = 255; - } - } else { - nComps = imgData->colorMap->getNumPixelComps(); - memcpy(colorLine, p, imgData->width * nComps); - } - - ++imgData->y; - return true; -} - -void SplashOutputDev::iccTransform(void *data, SplashBitmap *bitmap) { - SplashOutImageData *imgData = (SplashOutImageData *)data; - int nComps = imgData->colorMap->getNumPixelComps(); - - unsigned char *colorLine = (unsigned char *) gmalloc(nComps * bitmap->getWidth()); - unsigned char *rgbxLine = (imgData->colorMode == splashModeXBGR8) ? (unsigned char *) gmalloc(3 * bitmap->getWidth()) : nullptr; - for (int i = 0; i < bitmap->getHeight(); i++) { - unsigned char *p = bitmap->getDataPtr() + i * bitmap->getRowSize(); - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData->colorMap->getGrayLine(p, colorLine, bitmap->getWidth()); - memcpy(p, colorLine, nComps * bitmap->getWidth()); - break; - case splashModeRGB8: - case splashModeBGR8: - imgData->colorMap->getRGBLine(p, colorLine, bitmap->getWidth()); - memcpy(p, colorLine, nComps * bitmap->getWidth()); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - imgData->colorMap->getCMYKLine(p, colorLine, bitmap->getWidth()); - memcpy(p, colorLine, nComps * bitmap->getWidth()); - break; - case splashModeDeviceN8: - imgData->colorMap->getDeviceNLine(p, colorLine, bitmap->getWidth()); - memcpy(p, colorLine, nComps * bitmap->getWidth()); - break; -#endif - case splashModeXBGR8: - unsigned char *q; - unsigned char *b = p; - int x; - for (x = 0, q = rgbxLine; x < bitmap->getWidth(); ++x, b+=4) { - *q++ = b[2]; - *q++ = b[1]; - *q++ = b[0]; - } - imgData->colorMap->getRGBLine(rgbxLine, colorLine, bitmap->getWidth()); - b = p; - for (x = 0, q = colorLine; x < bitmap->getWidth(); ++x, b+=4) { - b[2] = *q++; - b[1] = *q++; - b[0] = *q++; - } - break; - } - } - gfree(colorLine); - if (rgbxLine != nullptr) - gfree(rgbxLine); -} -#endif - -bool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine, - unsigned char *alphaLine) { - SplashOutImageData *imgData = (SplashOutImageData *)data; - unsigned char *p, *aq; - SplashColorPtr q, col; - GfxRGB rgb; - GfxGray gray; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - unsigned char alpha; - int nComps, x, i; - - if (imgData->y == imgData->height) { - return false; - } - if (!(p = imgData->imgStr->getLine())) { - return false; - } - - nComps = imgData->colorMap->getNumPixelComps(); - - for (x = 0, q = colorLine, aq = alphaLine; - x < imgData->width; - ++x, p += nComps) { - alpha = 0; - for (i = 0; i < nComps; ++i) { - if (p[i] < imgData->maskColors[2*i] || - p[i] > imgData->maskColors[2*i+1]) { - alpha = 0xff; - break; - } - } - if (imgData->lookup) { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - *q++ = imgData->lookup[*p]; - break; - case splashModeRGB8: - case splashModeBGR8: - col = &imgData->lookup[3 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - break; - case splashModeXBGR8: - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = 255; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = col[3]; - break; - case splashModeDeviceN8: - col = &imgData->lookup[(SPOT_NCOMPS+4) * *p]; - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = col[cp]; - break; -#endif - } - *aq++ = alpha; - } else { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData->colorMap->getGray(p, &gray); - *q++ = colToByte(gray); - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - imgData->colorMap->getRGB(p, &rgb); - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); - *q++ = colToByte(rgb.b); - if (imgData->colorMode == splashModeXBGR8) *q++ = 255; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - imgData->colorMap->getCMYK(p, &cmyk); - *q++ = colToByte(cmyk.c); - *q++ = colToByte(cmyk.m); - *q++ = colToByte(cmyk.y); - *q++ = colToByte(cmyk.k); - break; - case splashModeDeviceN8: - imgData->colorMap->getDeviceN(p, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = colToByte(deviceN.c[cp]); - break; -#endif - } - *aq++ = alpha; - } - } - - ++imgData->y; - return true; -} - -struct TilingSplashOutBitmap { - SplashBitmap *bitmap; - SplashPattern *pattern; - SplashColorMode colorMode; - int paintType; - int repeatX; - int repeatY; - int y; -}; - -bool SplashOutputDev::tilingBitmapSrc(void *data, SplashColorPtr colorLine, - unsigned char *alphaLine) { - TilingSplashOutBitmap *imgData = (TilingSplashOutBitmap *)data; - - if (imgData->y == imgData->bitmap->getHeight()) { - imgData->repeatY--; - if (imgData->repeatY == 0) - return false; - imgData->y = 0; - } - - if (imgData->paintType == 1) { - const SplashColorMode cMode = imgData->bitmap->getMode(); - SplashColorPtr q = colorLine; - // For splashModeBGR8 and splashModeXBGR8 we need to use getPixel - // for the others we can use raw access - if (cMode == splashModeBGR8 || cMode == splashModeXBGR8) { - for (int m = 0; m < imgData->repeatX; m++) { - for (int x = 0; x < imgData->bitmap->getWidth(); x++) { - imgData->bitmap->getPixel(x, imgData->y, q); - q += splashColorModeNComps[cMode]; - } - } - } else { - const int n = imgData->bitmap->getRowSize(); - SplashColorPtr p; - for (int m = 0; m < imgData->repeatX; m++) { - p = imgData->bitmap->getDataPtr() + imgData->y * imgData->bitmap->getRowSize(); - for (int x = 0; x < n; ++x) { - *q++ = *p++; - } - } - } - if (alphaLine != nullptr) { - SplashColorPtr aq = alphaLine; - SplashColorPtr p; - const int n = imgData->bitmap->getWidth() - 1; - for (int m = 0; m < imgData->repeatX; m++) { - p = imgData->bitmap->getAlphaPtr() + imgData->y * imgData->bitmap->getWidth(); - for (int x = 0; x < n; ++x) { - *aq++ = *p++; - } - // This is a hack, because of how Splash antialias works if we overwrite the - // last alpha pixel of the tile most/all of the files look much better - *aq++ = (n == 0) ? *p : *(p - 1); - } - } - } else { - SplashColor col, pat; - SplashColorPtr dest = colorLine; - for (int m = 0; m < imgData->repeatX; m++) { - for (int x = 0; x < imgData->bitmap->getWidth(); x++) { - imgData->bitmap->getPixel(x, imgData->y, col); - imgData->pattern->getColor(x, imgData->y, pat); - for (int i = 0; i < splashColorModeNComps[imgData->colorMode]; ++i) { -#ifdef SPLASH_CMYK - if (imgData->colorMode == splashModeCMYK8 || imgData->colorMode == splashModeDeviceN8) - dest[i] = div255(pat[i] * (255 - col[0])); - else -#endif - dest[i] = 255 - div255((255 - pat[i]) * (255 - col[0])); - } - dest += splashColorModeNComps[imgData->colorMode]; - } - } - if (alphaLine != nullptr) { - const int y = (imgData->y == imgData->bitmap->getHeight() - 1 && imgData->y > 50) ? imgData->y - 1 : imgData->y; - SplashColorPtr aq = alphaLine; - SplashColorPtr p; - const int n = imgData->bitmap->getWidth(); - for (int m = 0; m < imgData->repeatX; m++) { - p = imgData->bitmap->getAlphaPtr() + y * imgData->bitmap->getWidth(); - for (int x = 0; x < n; ++x) { - *aq++ = *p++; - } - } - } - } - ++imgData->y; - return true; -} - -void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - bool interpolate, - int *maskColors, bool inlineImg) { - SplashCoord mat[6]; - SplashOutImageData imgData; - SplashColorMode srcMode; - SplashImageSource src; - SplashICCTransform tf; - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - bool grayIndexed = false; - GfxColor deviceN; -#endif - unsigned char pix; - int n, i; - - const double *ctm = state->getCTM(); - for (i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) return; - } - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - - imgData.imgStr = new ImageStream(str, width, - colorMap->getNumPixelComps(), - colorMap->getBits()); - imgData.imgStr->reset(); - imgData.colorMap = colorMap; - imgData.maskColors = maskColors; - imgData.colorMode = colorMode; - imgData.width = width; - imgData.height = height; - imgData.maskStr = nullptr; - imgData.maskColorMap = nullptr; - imgData.y = 0; - - // special case for one-channel (monochrome/gray/separation) images: - // build a lookup table here - imgData.lookup = nullptr; - if (colorMap->getNumPixelComps() == 1) { - n = 1 << colorMap->getBits(); - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData.lookup = (SplashColorPtr)gmalloc(n); - for (i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getGray(&pix, &gray); - imgData.lookup[i] = colToByte(gray); - } - break; - case splashModeRGB8: - case splashModeBGR8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 3); - for (i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[3*i] = colToByte(rgb.r); - imgData.lookup[3*i+1] = colToByte(rgb.g); - imgData.lookup[3*i+2] = colToByte(rgb.b); - } - break; - case splashModeXBGR8: - imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 4); - if (likely(imgData.lookup != nullptr)) { - for (i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[4*i] = colToByte(rgb.r); - imgData.lookup[4*i+1] = colToByte(rgb.g); - imgData.lookup[4*i+2] = colToByte(rgb.b); - imgData.lookup[4*i+3] = 255; - } - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray; - imgData.lookup = (SplashColorPtr)gmallocn(n, 4); - for (i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getCMYK(&pix, &cmyk); - if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) { - grayIndexed = false; - } - imgData.lookup[4*i] = colToByte(cmyk.c); - imgData.lookup[4*i+1] = colToByte(cmyk.m); - imgData.lookup[4*i+2] = colToByte(cmyk.y); - imgData.lookup[4*i+3] = colToByte(cmyk.k); - } - break; - case splashModeDeviceN8: - colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); - grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray; - imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4); - for (i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getCMYK(&pix, &cmyk); - if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) { - grayIndexed = false; - } - colorMap->getDeviceN(&pix, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - imgData.lookup[(SPOT_NCOMPS+4)*i +cp] = colToByte(deviceN.c[cp]); - } - break; -#endif - } - } - -#ifdef SPLASH_CMYK - setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr, grayIndexed); -#else - setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr); -#endif - - if (colorMode == splashModeMono1) { - srcMode = splashModeMono8; - } else { - srcMode = colorMode; - } -#ifdef USE_CMS - src = maskColors ? &alphaImageSrc : useIccImageSrc(&imgData) ? &iccImageSrc : &imageSrc; - tf = maskColors == nullptr && useIccImageSrc(&imgData) ? &iccTransform : nullptr; -#else - src = maskColors ? &alphaImageSrc : &imageSrc; - tf = nullptr; -#endif - splash->drawImage(src, tf, &imgData, srcMode, maskColors ? true : false, - width, height, mat, interpolate); - if (inlineImg) { - while (imgData.y < height) { - imgData.imgStr->getLine(); - ++imgData.y; - } - } - - gfree(imgData.lookup); - delete imgData.imgStr; - str->close(); -} - -struct SplashOutMaskedImageData { - ImageStream *imgStr; - GfxImageColorMap *colorMap; - SplashBitmap *mask; - SplashColorPtr lookup; - SplashColorMode colorMode; - int width, height, y; -}; - -bool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, - unsigned char *alphaLine) { - SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data; - unsigned char *p, *aq; - SplashColorPtr q, col; - GfxRGB rgb; - GfxGray gray; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - unsigned char alpha; - unsigned char *maskPtr; - int maskBit; - int nComps, x; - - if (imgData->y == imgData->height) { - return false; - } - if (!(p = imgData->imgStr->getLine())) { - return false; - } - - nComps = imgData->colorMap->getNumPixelComps(); - - maskPtr = imgData->mask->getDataPtr() + - imgData->y * imgData->mask->getRowSize(); - maskBit = 0x80; - for (x = 0, q = colorLine, aq = alphaLine; - x < imgData->width; - ++x, p += nComps) { - alpha = (*maskPtr & maskBit) ? 0xff : 0x00; - if (!(maskBit >>= 1)) { - ++maskPtr; - maskBit = 0x80; - } - if (imgData->lookup) { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - *q++ = imgData->lookup[*p]; - break; - case splashModeRGB8: - case splashModeBGR8: - col = &imgData->lookup[3 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - break; - case splashModeXBGR8: - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = 255; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; - *q++ = col[3]; - break; - case splashModeDeviceN8: - col = &imgData->lookup[(SPOT_NCOMPS+4) * *p]; - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = col[cp]; - break; -#endif - } - *aq++ = alpha; - } else { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData->colorMap->getGray(p, &gray); - *q++ = colToByte(gray); - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - imgData->colorMap->getRGB(p, &rgb); - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); - *q++ = colToByte(rgb.b); - if (imgData->colorMode == splashModeXBGR8) *q++ = 255; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - imgData->colorMap->getCMYK(p, &cmyk); - *q++ = colToByte(cmyk.c); - *q++ = colToByte(cmyk.m); - *q++ = colToByte(cmyk.y); - *q++ = colToByte(cmyk.k); - break; - case splashModeDeviceN8: - imgData->colorMap->getDeviceN(p, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *q++ = colToByte(deviceN.c[cp]); - break; -#endif - } - *aq++ = alpha; - } - } - - ++imgData->y; - return true; -} - -void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, - Stream *str, int width, int height, - GfxImageColorMap *colorMap, - bool interpolate, - Stream *maskStr, int maskWidth, - int maskHeight, bool maskInvert, - bool maskInterpolate) { - GfxImageColorMap *maskColorMap; - SplashCoord mat[6]; - SplashOutMaskedImageData imgData; - SplashOutImageMaskData imgMaskData; - SplashColorMode srcMode; - SplashBitmap *maskBitmap; - Splash *maskSplash; - SplashColor maskColor; - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - unsigned char pix; - int n, i; - -#ifdef SPLASH_CMYK - colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif - setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr); - - // If the mask is higher resolution than the image, use - // drawSoftMaskedImage() instead. - if (maskWidth > width || maskHeight > height) { - Object maskDecode(new Array((xref) ? xref : doc->getXRef())); - maskDecode.arrayAdd(Object(maskInvert ? 0 : 1)); - maskDecode.arrayAdd(Object(maskInvert ? 1 : 0)); - maskColorMap = new GfxImageColorMap(1, &maskDecode, - new GfxDeviceGrayColorSpace()); - drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate, - maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate); - delete maskColorMap; - - } else { - //----- scale the mask image to the same size as the source image - - mat[0] = (SplashCoord)width; - mat[1] = 0; - mat[2] = 0; - mat[3] = (SplashCoord)height; - mat[4] = 0; - mat[5] = 0; - imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1); - imgMaskData.imgStr->reset(); - imgMaskData.invert = maskInvert ? 0 : 1; - imgMaskData.width = maskWidth; - imgMaskData.height = maskHeight; - imgMaskData.y = 0; - maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, false); - if (!maskBitmap->getDataPtr()) { - delete maskBitmap; - width = height = 1; - maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, false); - } - maskSplash = new Splash(maskBitmap, false); - maskColor[0] = 0; - maskSplash->clear(maskColor); - maskColor[0] = 0xff; - maskSplash->setFillPattern(new SplashSolidColor(maskColor)); - maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, - maskWidth, maskHeight, mat, false); - delete imgMaskData.imgStr; - maskStr->close(); - delete maskSplash; - - //----- draw the source image - - const double *ctm = state->getCTM(); - for (i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) { - delete maskBitmap; - return; - } - } - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - - imgData.imgStr = new ImageStream(str, width, - colorMap->getNumPixelComps(), - colorMap->getBits()); - imgData.imgStr->reset(); - imgData.colorMap = colorMap; - imgData.mask = maskBitmap; - imgData.colorMode = colorMode; - imgData.width = width; - imgData.height = height; - imgData.y = 0; - - // special case for one-channel (monochrome/gray/separation) images: - // build a lookup table here - imgData.lookup = nullptr; - if (colorMap->getNumPixelComps() == 1) { - n = 1 << colorMap->getBits(); - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData.lookup = (SplashColorPtr)gmalloc(n); - for (i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getGray(&pix, &gray); - imgData.lookup[i] = colToByte(gray); - } - break; - case splashModeRGB8: - case splashModeBGR8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 3); - for (i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[3*i] = colToByte(rgb.r); - imgData.lookup[3*i+1] = colToByte(rgb.g); - imgData.lookup[3*i+2] = colToByte(rgb.b); - } - break; - case splashModeXBGR8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 4); - for (i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[4*i] = colToByte(rgb.r); - imgData.lookup[4*i+1] = colToByte(rgb.g); - imgData.lookup[4*i+2] = colToByte(rgb.b); - imgData.lookup[4*i+3] = 255; - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - imgData.lookup = (SplashColorPtr)gmallocn(n, 4); - for (i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getCMYK(&pix, &cmyk); - imgData.lookup[4*i] = colToByte(cmyk.c); - imgData.lookup[4*i+1] = colToByte(cmyk.m); - imgData.lookup[4*i+2] = colToByte(cmyk.y); - imgData.lookup[4*i+3] = colToByte(cmyk.k); - } - break; - case splashModeDeviceN8: - imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4); - for (i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getDeviceN(&pix, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]); - } - break; -#endif - } - } - - if (colorMode == splashModeMono1) { - srcMode = splashModeMono8; - } else { - srcMode = colorMode; - } - splash->drawImage(&maskedImageSrc, nullptr, &imgData, srcMode, true, - width, height, mat, interpolate); - delete maskBitmap; - gfree(imgData.lookup); - delete imgData.imgStr; - str->close(); - } -} - -void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object * /* ref */, - Stream *str, int width, int height, - GfxImageColorMap *colorMap, - bool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GfxImageColorMap *maskColorMap, - bool maskInterpolate) { - SplashCoord mat[6]; - SplashOutImageData imgData; - SplashOutImageData imgMaskData; - SplashColorMode srcMode; - SplashBitmap *maskBitmap; - Splash *maskSplash; - SplashColor maskColor; - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - unsigned char pix; - -#ifdef SPLASH_CMYK - colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif - setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr); - - const double *ctm = state->getCTM(); - for (int i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) return; - } - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - - //----- set up the soft mask - - if (maskColorMap->getMatteColor() != nullptr) { - int maskChars; - if (checkedMultiply(maskWidth, maskHeight, &maskChars)) { - return; - } - unsigned char *data = (unsigned char *) gmalloc(maskChars); - maskStr->reset(); - const int readChars = maskStr->doGetChars(maskChars, data); - if (unlikely(readChars < maskChars)) { - memset(&data[readChars], 0, maskChars - readChars); - } - maskStr->close(); - maskStr = new AutoFreeMemStream((char *)data, 0, maskChars, maskStr->getDictObject()->copy()); - } - imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, - maskColorMap->getNumPixelComps(), - maskColorMap->getBits()); - imgMaskData.imgStr->reset(); - imgMaskData.colorMap = maskColorMap; - imgMaskData.maskColors = nullptr; - imgMaskData.colorMode = splashModeMono8; - imgMaskData.width = maskWidth; - imgMaskData.height = maskHeight; - imgMaskData.y = 0; - imgMaskData.maskStr = nullptr; - imgMaskData.maskColorMap = nullptr; - const unsigned imgMaskDataLookupSize = 1 << maskColorMap->getBits(); - imgMaskData.lookup = (SplashColorPtr)gmalloc(imgMaskDataLookupSize); - for (unsigned i = 0; i < imgMaskDataLookupSize; ++i) { - pix = (unsigned char)i; - maskColorMap->getGray(&pix, &gray); - imgMaskData.lookup[i] = colToByte(gray); - } - maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), - 1, splashModeMono8, false); - maskSplash = new Splash(maskBitmap, vectorAntialias); - maskColor[0] = 0; - maskSplash->clear(maskColor); - maskSplash->drawImage(&imageSrc, nullptr, &imgMaskData, splashModeMono8, false, - maskWidth, maskHeight, mat, maskInterpolate); - delete imgMaskData.imgStr; - if (maskColorMap->getMatteColor() == nullptr) { - maskStr->close(); - } - gfree(imgMaskData.lookup); - delete maskSplash; - splash->setSoftMask(maskBitmap); - - //----- draw the source image - - imgData.imgStr = new ImageStream(str, width, - colorMap->getNumPixelComps(), - colorMap->getBits()); - imgData.imgStr->reset(); - imgData.colorMap = colorMap; - imgData.maskColors = nullptr; - imgData.colorMode = colorMode; - imgData.width = width; - imgData.height = height; - imgData.maskStr = nullptr; - imgData.maskColorMap = nullptr; - if (maskColorMap->getMatteColor() != nullptr) { - getMatteColor(colorMode, colorMap, maskColorMap->getMatteColor(), imgData.matteColor); - imgData.maskColorMap = maskColorMap; - imgData.maskStr = new ImageStream(maskStr, maskWidth, - maskColorMap->getNumPixelComps(), - maskColorMap->getBits()); - imgData.maskStr->reset(); - } - imgData.y = 0; - - // special case for one-channel (monochrome/gray/separation) images: - // build a lookup table here - imgData.lookup = nullptr; - if (colorMap->getNumPixelComps() == 1) { - const unsigned n = 1 << colorMap->getBits(); - switch (colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData.lookup = (SplashColorPtr)gmalloc(n); - for (unsigned i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getGray(&pix, &gray); - imgData.lookup[i] = colToByte(gray); - } - break; - case splashModeRGB8: - case splashModeBGR8: - imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 3); - if (likely(imgData.lookup != nullptr)) { - for (unsigned i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[3*i] = colToByte(rgb.r); - imgData.lookup[3*i+1] = colToByte(rgb.g); - imgData.lookup[3*i+2] = colToByte(rgb.b); - } - } - break; - case splashModeXBGR8: - imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 4); - if (likely(imgData.lookup != nullptr)) { - for (unsigned i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getRGB(&pix, &rgb); - imgData.lookup[4*i] = colToByte(rgb.r); - imgData.lookup[4*i+1] = colToByte(rgb.g); - imgData.lookup[4*i+2] = colToByte(rgb.b); - imgData.lookup[4*i+3] = 255; - } - } - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 4); - if (likely(imgData.lookup != nullptr)) { - for (unsigned i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getCMYK(&pix, &cmyk); - imgData.lookup[4*i] = colToByte(cmyk.c); - imgData.lookup[4*i+1] = colToByte(cmyk.m); - imgData.lookup[4*i+2] = colToByte(cmyk.y); - imgData.lookup[4*i+3] = colToByte(cmyk.k); - } - } - break; - case splashModeDeviceN8: - imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, SPOT_NCOMPS+4); - if (likely(imgData.lookup != nullptr)) { - for (unsigned i = 0; i < n; ++i) { - pix = (unsigned char)i; - colorMap->getDeviceN(&pix, &deviceN); - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]); - } - } - break; -#endif - } - } - - if (colorMode == splashModeMono1) { - srcMode = splashModeMono8; - } else { - srcMode = colorMode; - } - splash->drawImage(&imageSrc, nullptr, &imgData, srcMode, false, width, height, mat, interpolate); - splash->setSoftMask(nullptr); - gfree(imgData.lookup); - delete imgData.maskStr; - delete imgData.imgStr; - if (maskColorMap->getMatteColor() != nullptr) { - maskStr->close(); - delete maskStr; - } - str->close(); -} - -bool SplashOutputDev::checkTransparencyGroup(GfxState *state, bool knockout) { - if (state->getFillOpacity() != 1 || - state->getStrokeOpacity() != 1 || - state->getAlphaIsShape() || - state->getBlendMode() != gfxBlendNormal || - splash->getSoftMask() != nullptr || - knockout) - return true; - return transpGroupStack != nullptr && transpGroupStack->shape != nullptr; -} - -void SplashOutputDev::beginTransparencyGroup(GfxState *state, const double *bbox, - GfxColorSpace *blendingColorSpace, - bool isolated, bool knockout, - bool forSoftMask) { - SplashTransparencyGroup *transpGroup; - SplashColor color; - double xMin, yMin, xMax, yMax, x, y; - int tx, ty, w, h, i; - - // transform the bbox - state->transform(bbox[0], bbox[1], &x, &y); - xMin = xMax = x; - yMin = yMax = y; - state->transform(bbox[0], bbox[3], &x, &y); - if (x < xMin) { - xMin = x; - } else if (x > xMax) { - xMax = x; - } - if (y < yMin) { - yMin = y; - } else if (y > yMax) { - yMax = y; - } - state->transform(bbox[2], bbox[1], &x, &y); - if (x < xMin) { - xMin = x; - } else if (x > xMax) { - xMax = x; - } - if (y < yMin) { - yMin = y; - } else if (y > yMax) { - yMax = y; - } - state->transform(bbox[2], bbox[3], &x, &y); - if (x < xMin) { - xMin = x; - } else if (x > xMax) { - xMax = x; - } - if (y < yMin) { - yMin = y; - } else if (y > yMax) { - yMax = y; - } - tx = (int)floor(xMin); - if (tx < 0) { - tx = 0; - } else if (tx >= bitmap->getWidth()) { - tx = bitmap->getWidth() - 1; - } - ty = (int)floor(yMin); - if (ty < 0) { - ty = 0; - } else if (ty >= bitmap->getHeight()) { - ty = bitmap->getHeight() - 1; - } - w = (int)ceil(xMax) - tx + 1; - if (tx + w > bitmap->getWidth()) { - w = bitmap->getWidth() - tx; - } - if (w < 1) { - w = 1; - } - h = (int)ceil(yMax) - ty + 1; - if (ty + h > bitmap->getHeight()) { - h = bitmap->getHeight() - ty; - } - if (h < 1) { - h = 1; - } - - // push a new stack entry - transpGroup = new SplashTransparencyGroup(); - transpGroup->softmask = nullptr; - transpGroup->tx = tx; - transpGroup->ty = ty; - transpGroup->blendingColorSpace = blendingColorSpace; - transpGroup->isolated = isolated; - transpGroup->shape = (knockout && !isolated) ? SplashBitmap::copy(bitmap) : nullptr; - transpGroup->knockout = (knockout && isolated); - transpGroup->knockoutOpacity = 1.0; - transpGroup->next = transpGroupStack; - transpGroupStack = transpGroup; - - // save state - transpGroup->origBitmap = bitmap; - transpGroup->origSplash = splash; - transpGroup->fontAA = fontEngine->getAA(); - - //~ this handles the blendingColorSpace arg for soft masks, but - //~ not yet for transparency groups - - // switch to the blending color space - if (forSoftMask && isolated && blendingColorSpace) { - if (blendingColorSpace->getMode() == csDeviceGray || - blendingColorSpace->getMode() == csCalGray || - (blendingColorSpace->getMode() == csICCBased && - blendingColorSpace->getNComps() == 1)) { - colorMode = splashModeMono8; - } else if (blendingColorSpace->getMode() == csDeviceRGB || - blendingColorSpace->getMode() == csCalRGB || - (blendingColorSpace->getMode() == csICCBased && - blendingColorSpace->getNComps() == 3)) { - //~ does this need to use BGR8? - colorMode = splashModeRGB8; -#ifdef SPLASH_CMYK - } else if (blendingColorSpace->getMode() == csDeviceCMYK || - (blendingColorSpace->getMode() == csICCBased && - blendingColorSpace->getNComps() == 4)) { - colorMode = splashModeCMYK8; -#endif - } - } - - // create the temporary bitmap - bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, true, - bitmapTopDown, bitmap->getSeparationList()); - if (!bitmap->getDataPtr()) { - delete bitmap; - w = h = 1; - bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, true, bitmapTopDown); - } - splash = new Splash(bitmap, vectorAntialias, - transpGroup->origSplash->getScreen()); - if (transpGroup->next != nullptr && transpGroup->next->knockout) { - fontEngine->setAA(false); - } - splash->setThinLineMode(transpGroup->origSplash->getThinLineMode()); - splash->setMinLineWidth(s_minLineWidth); - //~ Acrobat apparently copies at least the fill and stroke colors, and - //~ maybe other state(?) -- but not the clipping path (and not sure - //~ what else) - //~ [this is likely the same situation as in type3D1()] - splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy()); - splash->setStrokePattern( - transpGroup->origSplash->getStrokePattern()->copy()); - if (isolated) { - for (i = 0; i < splashMaxColorComps; ++i) { - color[i] = 0; - } - if (colorMode == splashModeXBGR8) color[3] = 255; - splash->clear(color, 0); - } else { - SplashBitmap *shape = (knockout) ? transpGroup->shape : - (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->shape : transpGroup->origBitmap; - int shapeTx = (knockout) ? tx : - (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->tx + tx : tx; - int shapeTy = (knockout) ? ty : - (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->ty + ty : ty; - splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); - splash->setInNonIsolatedGroup(shape, shapeTx, shapeTy); - } - transpGroup->tBitmap = bitmap; - state->shiftCTMAndClip(-tx, -ty); - updateCTM(state, 0, 0, 0, 0, 0, 0); - ++nestCount; -} - -void SplashOutputDev::endTransparencyGroup(GfxState *state) { - // restore state - --nestCount; - delete splash; - bitmap = transpGroupStack->origBitmap; - colorMode = bitmap->getMode(); - splash = transpGroupStack->origSplash; - state->shiftCTMAndClip(transpGroupStack->tx, transpGroupStack->ty); - updateCTM(state, 0, 0, 0, 0, 0, 0); -} - -void SplashOutputDev::paintTransparencyGroup(GfxState *state, const double *bbox) { - SplashBitmap *tBitmap; - SplashTransparencyGroup *transpGroup; - bool isolated; - int tx, ty; - - tx = transpGroupStack->tx; - ty = transpGroupStack->ty; - tBitmap = transpGroupStack->tBitmap; - isolated = transpGroupStack->isolated; - - // paint the transparency group onto the parent bitmap - // - the clip path was set in the parent's state) - if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) { - SplashCoord knockoutOpacity = (transpGroupStack->next != nullptr) ? transpGroupStack->next->knockoutOpacity - : transpGroupStack->knockoutOpacity; - splash->setOverprintMask(0xffffffff, false); - splash->composite(tBitmap, 0, 0, tx, ty, - tBitmap->getWidth(), tBitmap->getHeight(), - false, !isolated, transpGroupStack->next != nullptr && transpGroupStack->next->knockout, knockoutOpacity); - fontEngine->setAA(transpGroupStack->fontAA); - if (transpGroupStack->next != nullptr && transpGroupStack->next->shape != nullptr) { - transpGroupStack->next->knockout = true; - } - } - - // pop the stack - transpGroup = transpGroupStack; - transpGroupStack = transpGroup->next; - if (transpGroupStack != nullptr && transpGroup->knockoutOpacity < transpGroupStack->knockoutOpacity) { - transpGroupStack->knockoutOpacity = transpGroup->knockoutOpacity; - } - delete transpGroup->shape; - delete transpGroup; - - delete tBitmap; -} - -void SplashOutputDev::setSoftMask(GfxState *state, const double *bbox, - bool alpha, Function *transferFunc, - GfxColor *backdropColor) { - SplashBitmap *softMask, *tBitmap; - Splash *tSplash; - SplashTransparencyGroup *transpGroup; - SplashColor color; - SplashColorPtr p; - GfxGray gray; - GfxRGB rgb; -#ifdef SPLASH_CMYK - GfxCMYK cmyk; - GfxColor deviceN; -#endif - double lum, lum2; - int tx, ty, x, y; - - tx = transpGroupStack->tx; - ty = transpGroupStack->ty; - tBitmap = transpGroupStack->tBitmap; - - // composite with backdrop color - if (!alpha && tBitmap->getMode() != splashModeMono1) { - //~ need to correctly handle the case where no blending color - //~ space is given - if (transpGroupStack->blendingColorSpace) { - tSplash = new Splash(tBitmap, vectorAntialias, - transpGroupStack->origSplash->getScreen()); - switch (tBitmap->getMode()) { - case splashModeMono1: - // transparency is not supported in mono1 mode - break; - case splashModeMono8: - transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray); - color[0] = colToByte(gray); - tSplash->compositeBackground(color); - break; - case splashModeXBGR8: - color[3] = 255; - // fallthrough - case splashModeRGB8: - case splashModeBGR8: - transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb); - color[0] = colToByte(rgb.r); - color[1] = colToByte(rgb.g); - color[2] = colToByte(rgb.b); - tSplash->compositeBackground(color); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk); - color[0] = colToByte(cmyk.c); - color[1] = colToByte(cmyk.m); - color[2] = colToByte(cmyk.y); - color[3] = colToByte(cmyk.k); - tSplash->compositeBackground(color); - break; - case splashModeDeviceN8: - transpGroupStack->blendingColorSpace->getDeviceN(backdropColor, &deviceN); - for (int cp=0; cp < SPOT_NCOMPS+4; cp++) - color[cp] = colToByte(deviceN.c[cp]); - tSplash->compositeBackground(color); - break; -#endif - } - delete tSplash; - } - } - - softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), - 1, splashModeMono8, false); - unsigned char fill = 0; - if (transpGroupStack->blendingColorSpace) { - transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray); - fill = colToByte(gray); - } - memset(softMask->getDataPtr(), fill, - softMask->getRowSize() * softMask->getHeight()); - p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; - int xMax = tBitmap->getWidth(); - int yMax = tBitmap->getHeight(); - if (xMax > bitmap->getWidth() - tx) xMax = bitmap->getWidth() - tx; - if (yMax > bitmap->getHeight() - ty) yMax = bitmap->getHeight() - ty; - for (y = 0; y < yMax; ++y) { - for (x = 0; x < xMax; ++x) { - if (alpha) { - if (transferFunc) { - lum = tBitmap->getAlpha(x, y) / 255.0; - transferFunc->transform(&lum, &lum2); - p[x] = (int)(lum2 * 255.0 + 0.5); - } else - p[x] = tBitmap->getAlpha(x, y); - } else { - tBitmap->getPixel(x, y, color); - // convert to luminosity - switch (tBitmap->getMode()) { - case splashModeMono1: - case splashModeMono8: - lum = color[0] / 255.0; - break; - case splashModeXBGR8: - case splashModeRGB8: - case splashModeBGR8: - lum = (0.3 / 255.0) * color[0] + - (0.59 / 255.0) * color[1] + - (0.11 / 255.0) * color[2]; - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - lum = (1 - color[3] / 255.0) - - (0.3 / 255.0) * color[0] - - (0.59 / 255.0) * color[1] - - (0.11 / 255.0) * color[2]; - if (lum < 0) { - lum = 0; - } - break; -#endif - } - if (transferFunc) { - transferFunc->transform(&lum, &lum2); - } else { - lum2 = lum; - } - p[x] = (int)(lum2 * 255.0 + 0.5); - } - } - p += softMask->getRowSize(); - } - splash->setSoftMask(softMask); - - // pop the stack - transpGroup = transpGroupStack; - transpGroupStack = transpGroup->next; - delete transpGroup; - - delete tBitmap; -} - -void SplashOutputDev::clearSoftMask(GfxState *state) { - splash->setSoftMask(nullptr); -} - -void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) { - splashColorCopy(paperColor, paperColorA); -} - -int SplashOutputDev::getBitmapWidth() { - return bitmap->getWidth(); -} - -int SplashOutputDev::getBitmapHeight() { - return bitmap->getHeight(); -} - -SplashBitmap *SplashOutputDev::takeBitmap() { - SplashBitmap *ret; - - ret = bitmap; - bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - return ret; -} - -void SplashOutputDev::getModRegion(int *xMin, int *yMin, - int *xMax, int *yMax) { - splash->getModRegion(xMin, yMin, xMax, yMax); -} - -void SplashOutputDev::clearModRegion() { - splash->clearModRegion(); -} - -#if 1 //~tmp: turn off anti-aliasing temporarily -bool SplashOutputDev::getVectorAntialias() { - return splash->getVectorAntialias(); -} - -void SplashOutputDev::setVectorAntialias(bool vaa) { - vaa = vaa && colorMode != splashModeMono1; - vectorAntialias = vaa; - splash->setVectorAntialias(vaa); -} -#endif - -void SplashOutputDev::setFreeTypeHinting(bool enable, bool enableSlightHintingA) -{ - enableFreeTypeHinting = enable; - enableSlightHinting = enableSlightHintingA; -} - -bool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *catalog, Object *str, - const double *ptm, int paintType, int /*tilingType*/, Dict *resDict, - const double *mat, const double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) -{ - PDFRectangle box; - Gfx *gfx; - Splash *formerSplash = splash; - SplashBitmap *formerBitmap = bitmap; - double width, height; - int surface_width, surface_height, result_width, result_height, i; - int repeatX, repeatY; - SplashCoord matc[6]; - Matrix m1; - const double *ctm; - double savedCTM[6]; - double kx, ky, sx, sy; - bool retValue = false; - - width = bbox[2] - bbox[0]; - height = bbox[3] - bbox[1]; - - if (xStep != width || yStep != height) - return false; - - // calculate offsets - ctm = state->getCTM(); - for (i = 0; i < 6; ++i) { - savedCTM[i] = ctm[i]; - } - state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - state->concatCTM(1, 0, 0, 1, bbox[0], bbox[1]); - ctm = state->getCTM(); - for (i = 0; i < 6; ++i) { - if (!std::isfinite(ctm[i])) { - state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); - return false; - } - } - matc[4] = x0 * xStep * ctm[0] + y0 * yStep * ctm[2] + ctm[4]; - matc[5] = x0 * xStep * ctm[1] + y0 * yStep * ctm[3] + ctm[5]; - if (splashAbs(ctm[1]) > splashAbs(ctm[0])) { - kx = -ctm[1]; - ky = ctm[2] - (ctm[0] * ctm[3]) / ctm[1]; - } else { - kx = ctm[0]; - ky = ctm[3] - (ctm[1] * ctm[2]) / ctm[0]; - } - result_width = (int) ceil(fabs(kx * width * (x1 - x0))); - result_height = (int) ceil(fabs(ky * height * (y1 - y0))); - kx = state->getHDPI() / 72.0; - ky = state->getVDPI() / 72.0; - m1.m[0] = (ptm[0] == 0) ? fabs(ptm[2]) * kx : fabs(ptm[0]) * kx; - m1.m[1] = 0; - m1.m[2] = 0; - m1.m[3] = (ptm[3] == 0) ? fabs(ptm[1]) * ky : fabs(ptm[3]) * ky; - m1.m[4] = 0; - m1.m[5] = 0; - m1.transform(width, height, &kx, &ky); - surface_width = (int) ceil (fabs(kx)); - surface_height = (int) ceil (fabs(ky)); - - sx = (double) result_width / (surface_width * (x1 - x0)); - sy = (double) result_height / (surface_height * (y1 - y0)); - m1.m[0] *= sx; - m1.m[3] *= sy; - m1.transform(width, height, &kx, &ky); - - if(fabs(kx) < 1 && fabs(ky) < 1) { - kx = std::min(kx, ky); - ky = 2 / kx; - m1.m[0] *= ky; - m1.m[3] *= ky; - m1.transform(width, height, &kx, &ky); - surface_width = (int) ceil (fabs(kx)); - surface_height = (int) ceil (fabs(ky)); - repeatX = x1 - x0; - repeatY = y1 - y0; - } else { - if ((unsigned long) surface_width * surface_height > 0x800000L) { - state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); - return false; - } - while(fabs(kx) > 16384 || fabs(ky) > 16384) { - // limit pattern bitmap size - m1.m[0] /= 2; - m1.m[3] /= 2; - m1.transform(width, height, &kx, &ky); - } - surface_width = (int) ceil (fabs(kx)); - surface_height = (int) ceil (fabs(ky)); - // adjust repeat values to completely fill region - repeatX = result_width / surface_width; - repeatY = result_height / surface_height; - if (surface_width * repeatX < result_width) - repeatX++; - if (surface_height * repeatY < result_height) - repeatY++; - if (x1 - x0 > repeatX) - repeatX = x1 - x0; - if (y1 - y0 > repeatY) - repeatY = y1 - y0; - } - // restore CTM and calculate rotate and scale with rounded matrix - state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); - state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - state->concatCTM(width * repeatX, 0, 0, height * repeatY, bbox[0], bbox[1]); - ctm = state->getCTM(); - matc[0] = ctm[0]; - matc[1] = ctm[1]; - matc[2] = ctm[2]; - matc[3] = ctm[3]; - - if (surface_width == 0 || surface_height == 0 || repeatX * repeatY <= 4) { - state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); - return false; - } - m1.transform(bbox[0], bbox[1], &kx, &ky); - m1.m[4] = -kx; - m1.m[5] = -ky; - - bitmap = new SplashBitmap(surface_width, surface_height, 1, - (paintType == 1) ? colorMode : splashModeMono8, true); - if (bitmap->getDataPtr() == nullptr) { - SplashBitmap *tBitmap = bitmap; - bitmap = formerBitmap; - delete tBitmap; - state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); - return false; - } - splash = new Splash(bitmap, true); - if (paintType == 2) { - SplashColor clearColor; -#ifdef SPLASH_CMYK - clearColor[0] = (colorMode == splashModeCMYK8 || colorMode == splashModeDeviceN8) ? 0x00 : 0xFF; -#else - clearColor[0] = 0xFF; -#endif - splash->clear(clearColor, 0); - } else { - splash->clear(paperColor, 0); - } - splash->setThinLineMode(formerSplash->getThinLineMode()); - splash->setMinLineWidth(s_minLineWidth); - - box.x1 = bbox[0]; box.y1 = bbox[1]; - box.x2 = bbox[2]; box.y2 = bbox[3]; - gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA); - // set pattern transformation matrix - gfx->getState()->setCTM(m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]); - updateCTM(gfx->getState(), m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]); - gfx->display(str); - delete splash; - splash = formerSplash; - TilingSplashOutBitmap imgData; - imgData.bitmap = bitmap; - imgData.paintType = paintType; - imgData.pattern = splash->getFillPattern(); - imgData.colorMode = colorMode; - imgData.y = 0; - imgData.repeatX = repeatX; - imgData.repeatY = repeatY; - SplashBitmap *tBitmap = bitmap; - bitmap = formerBitmap; - result_width = tBitmap->getWidth() * imgData.repeatX; - result_height = tBitmap->getHeight() * imgData.repeatY; - - if (splashAbs(matc[1]) > splashAbs(matc[0])) { - kx = -matc[1]; - ky = matc[2] - (matc[0] * matc[3]) / matc[1]; - } else { - kx = matc[0]; - ky = matc[3] - (matc[1] * matc[2]) / matc[0]; - } - kx = result_width / (fabs(kx) + 1); - ky = result_height / (fabs(ky) + 1); - state->concatCTM(kx, 0, 0, ky, 0, 0); - ctm = state->getCTM(); - matc[0] = ctm[0]; - matc[1] = ctm[1]; - matc[2] = ctm[2]; - matc[3] = ctm[3]; - bool minorAxisZero = matc[1] == 0 && matc[2] == 0; - if (matc[0] > 0 && minorAxisZero && matc[3] > 0) { - // draw the tiles - for (int y = 0; y < imgData.repeatY; ++y) { - for (int x = 0; x < imgData.repeatX; ++x) { - x0 = splashFloor(matc[4]) + x * tBitmap->getWidth(); - y0 = splashFloor(matc[5]) + y * tBitmap->getHeight(); - splash->blitImage(tBitmap, true, x0, y0); - } - } - retValue = true; - } else { - retValue = splash->drawImage(&tilingBitmapSrc, nullptr, &imgData, colorMode, true, result_width, result_height, matc, false, true) == splashOk; - } - delete tBitmap; - delete gfx; - return retValue; -} - -bool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) -{ - GfxColorSpaceMode shadingMode = shading->getColorSpace()->getMode(); - bool bDirectColorTranslation = false; // triggers an optimization. - switch (colorMode) { - case splashModeRGB8: - bDirectColorTranslation = (shadingMode == csDeviceRGB); - break; -#ifdef SPLASH_CMYK - case splashModeCMYK8: - case splashModeDeviceN8: - bDirectColorTranslation = (shadingMode == csDeviceCMYK); - break; -#endif - default: - break; - } - // restore vector antialias because we support it here - if (shading->isParameterized()) { - SplashGouraudColor *splashShading = new SplashGouraudPattern(bDirectColorTranslation, state, shading); - bool vaa = getVectorAntialias(); - bool retVal = false; - setVectorAntialias(true); - retVal = splash->gouraudTriangleShadedFill(splashShading); - setVectorAntialias(vaa); - delete splashShading; - return retVal; - } - return false; -} - -bool SplashOutputDev::univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax) { - double xMin, yMin, xMax, yMax; - bool vaa = getVectorAntialias(); - // restore vector antialias because we support it here - setVectorAntialias(true); - - bool retVal = false; - // get the clip region bbox - if (pattern->getShading()->getHasBBox()) { - pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax); - } else { - state->getClipBBox(&xMin, &yMin, &xMax, &yMax); - - xMin = floor (xMin); - yMin = floor (yMin); - xMax = ceil (xMax); - yMax = ceil (yMax); - - { - Matrix ctm, ictm; - double x[4], y[4]; - int i; - - state->getCTM(&ctm); - ctm.invertTo(&ictm); - - ictm.transform(xMin, yMin, &x[0], &y[0]); - ictm.transform(xMax, yMin, &x[1], &y[1]); - ictm.transform(xMin, yMax, &x[2], &y[2]); - ictm.transform(xMax, yMax, &x[3], &y[3]); - - xMin = xMax = x[0]; - yMin = yMax = y[0]; - for (i = 1; i < 4; i++) { - xMin = std::min(xMin, x[i]); - yMin = std::min(yMin, y[i]); - xMax = std::max(xMax, x[i]); - yMax = std::max(yMax, y[i]); - } - } - } - - // fill the region - state->moveTo(xMin, yMin); - state->lineTo(xMax, yMin); - state->lineTo(xMax, yMax); - state->lineTo(xMin, yMax); - state->closePath(); - SplashPath path = convertPath(state, state->getPath(), true); - -#ifdef SPLASH_CMYK - pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif - setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr); - retVal = (splash->shadedFill(&path, pattern->getShading()->getHasBBox(), pattern) == splashOk); - state->clearPath(); - setVectorAntialias(vaa); - - return retVal; -} - -bool SplashOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { - SplashFunctionPattern *pattern = new SplashFunctionPattern(colorMode, state, shading); - double xMin, yMin, xMax, yMax; - bool vaa = getVectorAntialias(); - // restore vector antialias because we support it here - setVectorAntialias(true); - - bool retVal = false; - // get the clip region bbox - if (pattern->getShading()->getHasBBox()) { - pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax); - } else { - state->getClipBBox(&xMin, &yMin, &xMax, &yMax); - - xMin = floor (xMin); - yMin = floor (yMin); - xMax = ceil (xMax); - yMax = ceil (yMax); - - { - Matrix ctm, ictm; - double x[4], y[4]; - int i; - - state->getCTM(&ctm); - ctm.invertTo(&ictm); - - ictm.transform(xMin, yMin, &x[0], &y[0]); - ictm.transform(xMax, yMin, &x[1], &y[1]); - ictm.transform(xMin, yMax, &x[2], &y[2]); - ictm.transform(xMax, yMax, &x[3], &y[3]); - - xMin = xMax = x[0]; - yMin = yMax = y[0]; - for (i = 1; i < 4; i++) { - xMin = std::min(xMin, x[i]); - yMin = std::min(yMin, y[i]); - xMax = std::max(xMax, x[i]); - yMax = std::max(yMax, y[i]); - } - } - } - - // fill the region - state->moveTo(xMin, yMin); - state->lineTo(xMax, yMin); - state->lineTo(xMax, yMax); - state->lineTo(xMin, yMax); - state->closePath(); - SplashPath path = convertPath(state, state->getPath(), true); - -#ifdef SPLASH_CMYK - pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); -#endif - setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(), - state->getOverprintMode(), nullptr); - retVal = (splash->shadedFill(&path, pattern->getShading()->getHasBBox(), pattern) == splashOk); - state->clearPath(); - setVectorAntialias(vaa); - - delete pattern; - - return retVal; -} - -bool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { - SplashAxialPattern *pattern = new SplashAxialPattern(colorMode, state, shading); - bool retVal = univariateShadedFill(state, pattern, tMin, tMax); - - delete pattern; - - return retVal; -} - -bool SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) { - SplashRadialPattern *pattern = new SplashRadialPattern(colorMode, state, shading); - bool retVal = univariateShadedFill(state, pattern, tMin, tMax); - - delete pattern; - - return retVal; -} diff --git a/test/bug-hunting/cve/CVE-2019-14494/SplashOutputDev.h b/test/bug-hunting/cve/CVE-2019-14494/SplashOutputDev.h deleted file mode 100644 index 6ab2c68d334..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14494/SplashOutputDev.h +++ /dev/null @@ -1,533 +0,0 @@ -//======================================================================== -// -// SplashOutputDev.h -// -// Copyright 2003 Glyph & Cog, LLC -// -//======================================================================== - -//======================================================================== -// -// Modified under the Poppler project - http://poppler.freedesktop.org -// -// All changes made under the Poppler project to this file are licensed -// under GPL version 2 or later -// -// Copyright (C) 2005 Takashi Iwai -// Copyright (C) 2009-2016 Thomas Freitag -// Copyright (C) 2009 Carlos Garcia Campos -// Copyright (C) 2010 Christian Feuersänger -// Copyright (C) 2011 Andreas Hartmetz -// Copyright (C) 2011 Andrea Canciani -// Copyright (C) 2011, 2017 Adrian Johnson -// Copyright (C) 2012, 2015, 2018 Albert Astals Cid -// Copyright (C) 2015, 2016 William Bader -// Copyright (C) 2018 Stefan Brüns -// -// To see a description of the changes please see the Changelog file that -// came with your tarball or type make ChangeLog if you are building from git -// -//======================================================================== - -#ifndef SPLASHOUTPUTDEV_H -#define SPLASHOUTPUTDEV_H - -#include "splash/SplashTypes.h" -#include "splash/SplashPattern.h" -#include "poppler-config.h" -#include "OutputDev.h" -#include "GfxState.h" -#include "GlobalParams.h" - -class PDFDoc; -class Gfx8BitFont; -class SplashBitmap; -class Splash; -class SplashPath; -class SplashFontEngine; -class SplashFont; -class T3FontCache; -struct T3FontCacheTag; -struct T3GlyphStack; -struct SplashTransparencyGroup; - -//------------------------------------------------------------------------ -// Splash dynamic pattern -//------------------------------------------------------------------------ - -class SplashFunctionPattern : public SplashPattern { -public: - - SplashFunctionPattern(SplashColorMode colorMode, GfxState *state, GfxFunctionShading *shading); - - SplashPattern *copy() override { - return new SplashFunctionPattern(colorMode, state, (GfxFunctionShading *) shading); - } - - ~SplashFunctionPattern(); - - bool testPosition(int x, int y) override { - return true; - } - - bool isStatic() override { - return false; - } - - bool getColor(int x, int y, SplashColorPtr c) override; - - virtual GfxFunctionShading *getShading() { - return shading; - } - - bool isCMYK() override { - return gfxMode == csDeviceCMYK; - } - -protected: - Matrix ictm; - double xMin, yMin, xMax, yMax; - GfxFunctionShading *shading; - GfxState *state; - SplashColorMode colorMode; - GfxColorSpaceMode gfxMode; -}; - -class SplashUnivariatePattern : public SplashPattern { -public: - - SplashUnivariatePattern(SplashColorMode colorMode, GfxState *state, GfxUnivariateShading *shading); - - ~SplashUnivariatePattern(); - - bool getColor(int x, int y, SplashColorPtr c) override; - - bool testPosition(int x, int y) override; - - bool isStatic() override { - return false; - } - - virtual bool getParameter(double xs, double ys, double *t) = 0; - - virtual GfxUnivariateShading *getShading() { - return shading; - } - - bool isCMYK() override { - return gfxMode == csDeviceCMYK; - } - -protected: - Matrix ictm; - double t0, t1, dt; - GfxUnivariateShading *shading; - GfxState *state; - SplashColorMode colorMode; - GfxColorSpaceMode gfxMode; -}; - -class SplashAxialPattern : public SplashUnivariatePattern { -public: - - SplashAxialPattern(SplashColorMode colorMode, GfxState *state, GfxAxialShading *shading); - - SplashPattern *copy() override { - return new SplashAxialPattern(colorMode, state, (GfxAxialShading *) shading); - } - - ~SplashAxialPattern(); - - bool getParameter(double xs, double ys, double *t) override; - -private: - double x0, y0, x1, y1; - double dx, dy, mul; -}; - -// see GfxState.h, GfxGouraudTriangleShading -class SplashGouraudPattern : public SplashGouraudColor { -public: - - SplashGouraudPattern(bool bDirectColorTranslation, GfxState *state, GfxGouraudTriangleShading *shading); - - SplashPattern *copy() override { - return new SplashGouraudPattern(bDirectColorTranslation, state, shading); - } - - ~SplashGouraudPattern(); - - bool getColor(int x, int y, SplashColorPtr c) override { - return false; - } - - bool testPosition(int x, int y) override { - return false; - } - - bool isStatic() override { - return false; - } - - bool isCMYK() override { - return gfxMode == csDeviceCMYK; - } - - bool isParameterized() override { - return shading->isParameterized(); - } - int getNTriangles() override { - return shading->getNTriangles(); - } - void getTriangle(int i, double *x0, double *y0, double *color0, - double *x1, double *y1, double *color1, - double *x2, double *y2, double *color2) override - { - shading->getTriangle(i, x0, y0, color0, x1, y1, color1, x2, y2, color2); - } - - void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c) override; - -private: - GfxGouraudTriangleShading *shading; - GfxState *state; - bool bDirectColorTranslation; - GfxColorSpaceMode gfxMode; -}; - -// see GfxState.h, GfxRadialShading -class SplashRadialPattern : public SplashUnivariatePattern { -public: - - SplashRadialPattern(SplashColorMode colorMode, GfxState *state, GfxRadialShading *shading); - - SplashPattern *copy() override { - return new SplashRadialPattern(colorMode, state, (GfxRadialShading *) shading); - } - - ~SplashRadialPattern(); - - bool getParameter(double xs, double ys, double *t) override; - -private: - double x0, y0, r0, dx, dy, dr; - double a, inva; -}; - -//------------------------------------------------------------------------ - -// number of Type 3 fonts to cache -#define splashOutT3FontCacheSize 8 - -//------------------------------------------------------------------------ -// SplashOutputDev -//------------------------------------------------------------------------ - -class SplashOutputDev : public OutputDev { -public: - - // Constructor. - SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, - bool reverseVideoA, SplashColorPtr paperColorA, - bool bitmapTopDownA = true, - SplashThinLineMode thinLineMode = splashThinLineDefault, - bool overprintPreviewA = globalParams->getOverprintPreview()); - - // Destructor. - ~SplashOutputDev(); - - //----- get info about output device - - // Does this device use tilingPatternFill()? If this returns false, - // tiling pattern fills will be reduced to a series of other drawing - // operations. - bool useTilingPatternFill() override { - return true; - } - - // Does this device use functionShadedFill(), axialShadedFill(), and - // radialShadedFill()? If this returns false, these shaded fills - // will be reduced to a series of other drawing operations. - bool useShadedFills(int type) override - { - return (type >= 1 && type <= 5) ? true : false; - } - - // Does this device use upside-down coordinates? - // (Upside-down means (0,0) is the top left corner of the page.) - bool upsideDown() override { - return bitmapTopDown ^ bitmapUpsideDown; - } - - // Does this device use drawChar() or drawString()? - bool useDrawChar() override { - return true; - } - - // Does this device use beginType3Char/endType3Char? Otherwise, - // text in Type 3 fonts will be drawn with drawChar/drawString. - bool interpretType3Chars() override { - return true; - } - - //----- initialization and control - - // Start a page. - void startPage(int pageNum, GfxState *state, XRef *xref) override; - - // End a page. - void endPage() override; - - //----- save/restore graphics state - void saveState(GfxState *state) override; - void restoreState(GfxState *state) override; - - //----- update graphics state - void updateAll(GfxState *state) override; - void updateCTM(GfxState *state, double m11, double m12, - double m21, double m22, double m31, double m32) override; - void updateLineDash(GfxState *state) override; - void updateFlatness(GfxState *state) override; - void updateLineJoin(GfxState *state) override; - void updateLineCap(GfxState *state) override; - void updateMiterLimit(GfxState *state) override; - void updateLineWidth(GfxState *state) override; - void updateStrokeAdjust(GfxState *state) override; - void updateFillColorSpace(GfxState *state) override; - void updateStrokeColorSpace(GfxState *state) override; - void updateFillColor(GfxState *state) override; - void updateStrokeColor(GfxState *state) override; - void updateBlendMode(GfxState *state) override; - void updateFillOpacity(GfxState *state) override; - void updateStrokeOpacity(GfxState *state) override; - void updatePatternOpacity(GfxState *state) override; - void clearPatternOpacity(GfxState *state) override; - void updateFillOverprint(GfxState *state) override; - void updateStrokeOverprint(GfxState *state) override; - void updateOverprintMode(GfxState *state) override; - void updateTransfer(GfxState *state) override; - - //----- update text state - void updateFont(GfxState *state) override; - - //----- path painting - void stroke(GfxState *state) override; - void fill(GfxState *state) override; - void eoFill(GfxState *state) override; - bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *catalog, Object *str, - const double *pmat, int paintType, int tilingType, Dict *resDict, - const double *mat, const double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) override; - bool functionShadedFill(GfxState *state, GfxFunctionShading *shading) override; - bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override; - bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) override; - bool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) override; - - //----- path clipping - void clip(GfxState *state) override; - void eoClip(GfxState *state) override; - void clipToStrokePath(GfxState *state) override; - - //----- text drawing - void drawChar(GfxState *state, double x, double y, - double dx, double dy, - double originX, double originY, - CharCode code, int nBytes, Unicode *u, int uLen) override; - bool beginType3Char(GfxState *state, double x, double y, - double dx, double dy, - CharCode code, Unicode *u, int uLen) override; - void endType3Char(GfxState *state) override; - void beginTextObject(GfxState *state) override; - void endTextObject(GfxState *state) override; - - //----- image drawing - void drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, bool invert, - bool interpolate, bool inlineImg) override; - void setSoftMaskFromImageMask(GfxState *state, - Object *ref, Stream *str, - int width, int height, bool invert, - bool inlineImg, double *baseMatrix) override; - void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) override; - void drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - bool interpolate, int *maskColors, bool inlineImg) override; - void drawMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - bool interpolate, - Stream *maskStr, int maskWidth, int maskHeight, - bool maskInvert, bool maskInterpolate) override; - void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - bool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GfxImageColorMap *maskColorMap, - bool maskInterpolate) override; - - //----- Type 3 font operators - void type3D0(GfxState *state, double wx, double wy) override; - void type3D1(GfxState *state, double wx, double wy, - double llx, double lly, double urx, double ury) override; - - //----- transparency groups and soft masks - bool checkTransparencyGroup(GfxState *state, bool knockout) override; - void beginTransparencyGroup(GfxState *state, const double *bbox, - GfxColorSpace *blendingColorSpace, - bool isolated, bool knockout, - bool forSoftMask) override; - void endTransparencyGroup(GfxState *state) override; - void paintTransparencyGroup(GfxState *state, const double *bbox) override; - void setSoftMask(GfxState *state, const double *bbox, bool alpha, - Function *transferFunc, GfxColor *backdropColor) override; - void clearSoftMask(GfxState *state) override; - - //----- special access - - // Called to indicate that a new PDF document has been loaded. - void startDoc(PDFDoc *docA); - - void setPaperColor(SplashColorPtr paperColorA); - - bool isReverseVideo() { - return reverseVideo; - } - void setReverseVideo(bool reverseVideoA) { - reverseVideo = reverseVideoA; - } - - // Get the bitmap and its size. - SplashBitmap *getBitmap() { - return bitmap; - } - int getBitmapWidth(); - int getBitmapHeight(); - - // Returns the last rasterized bitmap, transferring ownership to the - // caller. - SplashBitmap *takeBitmap(); - - // Set this flag to true to generate an upside-down bitmap (useful - // for Windows BMP files). - void setBitmapUpsideDown(bool f) { - bitmapUpsideDown = f; - } - - // Get the Splash object. - Splash *getSplash() { - return splash; - } - - // Get the modified region. - void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax); - - // Clear the modified region. - void clearModRegion(); - - SplashFont *getCurrentFont() { - return font; - } - - // If is true, don't draw horizontal text. - // If is true, don't draw rotated (non-horizontal) text. - void setSkipText(bool skipHorizTextA, bool skipRotatedTextA) - { - skipHorizText = skipHorizTextA; skipRotatedText = skipRotatedTextA; - } - - int getNestCount() { - return nestCount; - } - -#if 1 //~tmp: turn off anti-aliasing temporarily - bool getVectorAntialias() override; - void setVectorAntialias(bool vaa) override; -#endif - - bool getFontAntialias() { - return fontAntialias; - } - void setFontAntialias(bool anti) { - fontAntialias = anti; - } - - void setFreeTypeHinting(bool enable, bool enableSlightHinting); - -protected: - void doUpdateFont(GfxState *state); - -private: - bool univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax); - - void setupScreenParams(double hDPI, double vDPI); - SplashPattern *getColor(GfxGray gray); - SplashPattern *getColor(GfxRGB *rgb); -#ifdef SPLASH_CMYK - SplashPattern *getColor(GfxCMYK *cmyk); - SplashPattern *getColor(GfxColor *deviceN); -#endif - static void getMatteColor( SplashColorMode colorMode, GfxImageColorMap *colorMap, const GfxColor * matteColor, SplashColor splashMatteColor); - void setOverprintMask(GfxColorSpace *colorSpace, bool overprintFlag, - int overprintMode, const GfxColor *singleColor, bool grayIndexed = false); - SplashPath convertPath(GfxState *state, GfxPath *path, - bool dropEmptySubpaths); - void drawType3Glyph(GfxState *state, T3FontCache *t3Font, - T3FontCacheTag *tag, unsigned char *data); -#ifdef USE_CMS - bool useIccImageSrc(void *data); - static void iccTransform(void *data, SplashBitmap *bitmap); - static bool iccImageSrc(void *data, SplashColorPtr colorLine, - unsigned char *alphaLine); -#endif - static bool imageMaskSrc(void *data, SplashColorPtr line); - static bool imageSrc(void *data, SplashColorPtr colorLine, - unsigned char *alphaLine); - static bool alphaImageSrc(void *data, SplashColorPtr line, - unsigned char *alphaLine); - static bool maskedImageSrc(void *data, SplashColorPtr line, - unsigned char *alphaLine); - static bool tilingBitmapSrc(void *data, SplashColorPtr line, - unsigned char *alphaLine); - - bool keepAlphaChannel; // don't fill with paper color, keep alpha channel - - SplashColorMode colorMode; - int bitmapRowPad; - bool bitmapTopDown; - bool bitmapUpsideDown; - bool fontAntialias; - bool vectorAntialias; - bool overprintPreview; - bool enableFreeTypeHinting; - bool enableSlightHinting; - bool reverseVideo; // reverse video mode - SplashColor paperColor; // paper color - SplashScreenParams screenParams; - bool skipHorizText; - bool skipRotatedText; - - PDFDoc *doc; // the current document - XRef *xref; // the xref of the current document - - SplashBitmap *bitmap; - Splash *splash; - SplashFontEngine *fontEngine; - - T3FontCache * // Type 3 font cache - t3FontCache[splashOutT3FontCacheSize]; - int nT3Fonts; // number of valid entries in t3FontCache - T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack - - SplashFont *font; // current font - bool needFontUpdate; // set when the font needs to be updated - SplashPath *textClipPath; // clipping path built with text object - - SplashTransparencyGroup * // transparency group stack - transpGroupStack; - int nestCount; -}; - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-14494/expected.txt b/test/bug-hunting/cve/CVE-2019-14494/expected.txt deleted file mode 100644 index a0c65509c58..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14494/expected.txt +++ /dev/null @@ -1,3 +0,0 @@ -SplashOutputDev.cc:4584:bughuntingDivByZero -SplashOutputDev.cc:4585:bughuntingDivByZero - diff --git a/test/bug-hunting/cve/CVE-2019-14981/expected.txt b/test/bug-hunting/cve/CVE-2019-14981/expected.txt deleted file mode 100644 index c0845b946f3..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14981/expected.txt +++ /dev/null @@ -1 +0,0 @@ -feature.c:2291:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-14981/feature.c b/test/bug-hunting/cve/CVE-2019-14981/feature.c deleted file mode 100644 index e68f37e21ae..00000000000 --- a/test/bug-hunting/cve/CVE-2019-14981/feature.c +++ /dev/null @@ -1,2338 +0,0 @@ -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % FFFFF EEEEE AAA TTTTT U U RRRR EEEEE % - % F E A A T U U R R E % - % FFF EEE AAAAA T U U RRRR EEE % - % F E A A T U U R R E % - % F EEEEE A A T UUU R R EEEEE % - % % - % % - % MagickCore Image Feature Methods % - % % - % Software Design % - % Cristy % - % July 1992 % - % % - % % - % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization % - % dedicated to making software imaging solutions freely available. % - % % - % You may not use this file except in compliance with the License. You may % - % obtain a copy of the License at % - % % - % https://imagemagick.org/script/license.php % - % % - % Unless required by applicable law or agreed to in writing, software % - % distributed under the License is distributed on an "AS IS" BASIS, % - % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % - % See the License for the specific language governing permissions and % - % limitations under the License. % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % - % - */ - -/* - Include declarations. - */ -#include "MagickCore/studio.h" -#include "MagickCore/animate.h" -#include "MagickCore/artifact.h" -#include "MagickCore/blob.h" -#include "MagickCore/blob-private.h" -#include "MagickCore/cache.h" -#include "MagickCore/cache-private.h" -#include "MagickCore/cache-view.h" -#include "MagickCore/channel.h" -#include "MagickCore/client.h" -#include "MagickCore/color.h" -#include "MagickCore/color-private.h" -#include "MagickCore/colorspace.h" -#include "MagickCore/colorspace-private.h" -#include "MagickCore/composite.h" -#include "MagickCore/composite-private.h" -#include "MagickCore/compress.h" -#include "MagickCore/constitute.h" -#include "MagickCore/display.h" -#include "MagickCore/draw.h" -#include "MagickCore/enhance.h" -#include "MagickCore/exception.h" -#include "MagickCore/exception-private.h" -#include "MagickCore/feature.h" -#include "MagickCore/gem.h" -#include "MagickCore/geometry.h" -#include "MagickCore/list.h" -#include "MagickCore/image-private.h" -#include "MagickCore/magic.h" -#include "MagickCore/magick.h" -#include "MagickCore/matrix.h" -#include "MagickCore/memory_.h" -#include "MagickCore/module.h" -#include "MagickCore/monitor.h" -#include "MagickCore/monitor-private.h" -#include "MagickCore/morphology-private.h" -#include "MagickCore/option.h" -#include "MagickCore/paint.h" -#include "MagickCore/pixel-accessor.h" -#include "MagickCore/profile.h" -#include "MagickCore/property.h" -#include "MagickCore/quantize.h" -#include "MagickCore/quantum-private.h" -#include "MagickCore/random_.h" -#include "MagickCore/resource_.h" -#include "MagickCore/segment.h" -#include "MagickCore/semaphore.h" -#include "MagickCore/signature-private.h" -#include "MagickCore/string_.h" -#include "MagickCore/thread-private.h" -#include "MagickCore/timer.h" -#include "MagickCore/utility.h" -#include "MagickCore/version.h" - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % C a n n y E d g e I m a g e % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % CannyEdgeImage() uses a multi-stage algorithm to detect a wide range of - % edges in images. - % - % The format of the CannyEdgeImage method is: - % - % Image *CannyEdgeImage(const Image *image,const double radius, - % const double sigma,const double lower_percent, - % const double upper_percent,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image. - % - % o radius: the radius of the gaussian smoothing filter. - % - % o sigma: the sigma of the gaussian smoothing filter. - % - % o lower_percent: percentage of edge pixels in the lower threshold. - % - % o upper_percent: percentage of edge pixels in the upper threshold. - % - % o exception: return any errors or warnings in this structure. - % - */ - -typedef struct _CannyInfo -{ - double - magnitude, - intensity; - - int - orientation; - - ssize_t - x, - y; -} CannyInfo; - -static inline MagickBooleanType IsAuthenticPixel(const Image *image, - const ssize_t x,const ssize_t y) -{ - if ((x < 0) || (x >= (ssize_t) image->columns)) - return(MagickFalse); - if ((y < 0) || (y >= (ssize_t) image->rows)) - return(MagickFalse); - return(MagickTrue); -} - -static MagickBooleanType TraceEdges(Image *edge_image,CacheView *edge_view, - MatrixInfo *canny_cache,const ssize_t x,const ssize_t y, - const double lower_threshold,ExceptionInfo *exception) -{ - CannyInfo - edge, - pixel; - - MagickBooleanType - status; - - register Quantum - *q; - - register ssize_t - i; - - q=GetCacheViewAuthenticPixels(edge_view,x,y,1,1,exception); - if (q == (Quantum *) NULL) - return(MagickFalse); - *q=QuantumRange; - status=SyncCacheViewAuthenticPixels(edge_view,exception); - if (status == MagickFalse) - return(MagickFalse); - if (GetMatrixElement(canny_cache,0,0,&edge) == MagickFalse) - return(MagickFalse); - edge.x=x; - edge.y=y; - if (SetMatrixElement(canny_cache,0,0,&edge) == MagickFalse) - return(MagickFalse); - for (i=1; i != 0;) - { - ssize_t - v; - - i--; - status=GetMatrixElement(canny_cache,i,0,&edge); - if (status == MagickFalse) - return(MagickFalse); - for (v=(-1); v <= 1; v++) - { - ssize_t - u; - - for (u=(-1); u <= 1; u++) - { - if ((u == 0) && (v == 0)) - continue; - if (IsAuthenticPixel(edge_image,edge.x+u,edge.y+v) == MagickFalse) - continue; - /* - Not an edge if gradient value is below the lower threshold. - */ - q=GetCacheViewAuthenticPixels(edge_view,edge.x+u,edge.y+v,1,1, - exception); - if (q == (Quantum *) NULL) - return(MagickFalse); - status=GetMatrixElement(canny_cache,edge.x+u,edge.y+v,&pixel); - if (status == MagickFalse) - return(MagickFalse); - if ((GetPixelIntensity(edge_image,q) == 0.0) && - (pixel.intensity >= lower_threshold)) - { - *q=QuantumRange; - status=SyncCacheViewAuthenticPixels(edge_view,exception); - if (status == MagickFalse) - return(MagickFalse); - edge.x+=u; - edge.y+=v; - status=SetMatrixElement(canny_cache,i,0,&edge); - if (status == MagickFalse) - return(MagickFalse); - i++; - } - } - } - } - return(MagickTrue); -} - -MagickExport Image *CannyEdgeImage(const Image *image,const double radius, - const double sigma,const double lower_percent,const double upper_percent, - ExceptionInfo *exception) -{ -#define CannyEdgeImageTag "CannyEdge/Image" - - CacheView - *edge_view; - - CannyInfo - element; - - char - geometry[MagickPathExtent]; - - double - lower_threshold, - max, - min, - upper_threshold; - - Image - *edge_image; - - KernelInfo - *kernel_info; - - MagickBooleanType - status; - - MagickOffsetType - progress; - - MatrixInfo - *canny_cache; - - ssize_t - y; - - assert(image != (const Image *) NULL); - assert(image->signature == MagickCoreSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - /* - Filter out noise. - */ - (void) FormatLocaleString(geometry,MagickPathExtent, - "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma); - kernel_info=AcquireKernelInfo(geometry,exception); - if (kernel_info == (KernelInfo *) NULL) - ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); - edge_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,exception); - kernel_info=DestroyKernelInfo(kernel_info); - if (edge_image == (Image *) NULL) - return((Image *) NULL); - if (TransformImageColorspace(edge_image,GRAYColorspace,exception) == MagickFalse) - { - edge_image=DestroyImage(edge_image); - return((Image *) NULL); - } - (void) SetImageAlphaChannel(edge_image,OffAlphaChannel,exception); - /* - Find the intensity gradient of the image. - */ - canny_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows, - sizeof(CannyInfo),exception); - if (canny_cache == (MatrixInfo *) NULL) - { - edge_image=DestroyImage(edge_image); - return((Image *) NULL); - } - status=MagickTrue; - edge_view=AcquireVirtualCacheView(edge_image,exception); -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static) shared(status) \ - magick_number_threads(edge_image,edge_image,edge_image->rows,1) -#endif - for (y=0; y < (ssize_t) edge_image->rows; y++) - { - register const Quantum - *magick_restrict p; - - register ssize_t - x; - - if (status == MagickFalse) - continue; - p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns+1,2, - exception); - if (p == (const Quantum *) NULL) - { - status=MagickFalse; - continue; - } - for (x=0; x < (ssize_t) edge_image->columns; x++) - { - CannyInfo - pixel; - - double - dx, - dy; - - register const Quantum - *magick_restrict kernel_pixels; - - ssize_t - v; - - static double - Gx[2][2] = - { - { -1.0, +1.0 }, - { -1.0, +1.0 } - }, - Gy[2][2] = - { - { +1.0, +1.0 }, - { -1.0, -1.0 } - }; - - (void) memset(&pixel,0,sizeof(pixel)); - dx=0.0; - dy=0.0; - kernel_pixels=p; - for (v=0; v < 2; v++) - { - ssize_t - u; - - for (u=0; u < 2; u++) - { - double - intensity; - - intensity=GetPixelIntensity(edge_image,kernel_pixels+u); - dx+=0.5*Gx[v][u]*intensity; - dy+=0.5*Gy[v][u]*intensity; - } - kernel_pixels+=edge_image->columns+1; - } - pixel.magnitude=hypot(dx,dy); - pixel.orientation=0; - if (fabs(dx) > MagickEpsilon) - { - double - slope; - - slope=dy/dx; - if (slope < 0.0) - { - if (slope < -2.41421356237) - pixel.orientation=0; - else - if (slope < -0.414213562373) - pixel.orientation=1; - else - pixel.orientation=2; - } - else - { - if (slope > 2.41421356237) - pixel.orientation=0; - else - if (slope > 0.414213562373) - pixel.orientation=3; - else - pixel.orientation=2; - } - } - if (SetMatrixElement(canny_cache,x,y,&pixel) == MagickFalse) - continue; - p+=GetPixelChannels(edge_image); - } - } - edge_view=DestroyCacheView(edge_view); - /* - Non-maxima suppression, remove pixels that are not considered to be part - of an edge. - */ - progress=0; - (void) GetMatrixElement(canny_cache,0,0,&element); - max=element.intensity; - min=element.intensity; - edge_view=AcquireAuthenticCacheView(edge_image,exception); -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static) shared(status) \ - magick_number_threads(edge_image,edge_image,edge_image->rows,1) -#endif - for (y=0; y < (ssize_t) edge_image->rows; y++) - { - register Quantum - *magick_restrict q; - - register ssize_t - x; - - if (status == MagickFalse) - continue; - q=GetCacheViewAuthenticPixels(edge_view,0,y,edge_image->columns,1, - exception); - if (q == (Quantum *) NULL) - { - status=MagickFalse; - continue; - } - for (x=0; x < (ssize_t) edge_image->columns; x++) - { - CannyInfo - alpha_pixel, - beta_pixel, - pixel; - - (void) GetMatrixElement(canny_cache,x,y,&pixel); - switch (pixel.orientation) - { - case 0: - default: - { - /* - 0 degrees, north and south. - */ - (void) GetMatrixElement(canny_cache,x,y-1,&alpha_pixel); - (void) GetMatrixElement(canny_cache,x,y+1,&beta_pixel); - break; - } - case 1: - { - /* - 45 degrees, northwest and southeast. - */ - (void) GetMatrixElement(canny_cache,x-1,y-1,&alpha_pixel); - (void) GetMatrixElement(canny_cache,x+1,y+1,&beta_pixel); - break; - } - case 2: - { - /* - 90 degrees, east and west. - */ - (void) GetMatrixElement(canny_cache,x-1,y,&alpha_pixel); - (void) GetMatrixElement(canny_cache,x+1,y,&beta_pixel); - break; - } - case 3: - { - /* - 135 degrees, northeast and southwest. - */ - (void) GetMatrixElement(canny_cache,x+1,y-1,&beta_pixel); - (void) GetMatrixElement(canny_cache,x-1,y+1,&alpha_pixel); - break; - } - } - pixel.intensity=pixel.magnitude; - if ((pixel.magnitude < alpha_pixel.magnitude) || - (pixel.magnitude < beta_pixel.magnitude)) - pixel.intensity=0; - (void) SetMatrixElement(canny_cache,x,y,&pixel); -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp critical (MagickCore_CannyEdgeImage) -#endif - { - if (pixel.intensity < min) - min=pixel.intensity; - if (pixel.intensity > max) - max=pixel.intensity; - } - *q=0; - q+=GetPixelChannels(edge_image); - } - if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse) - status=MagickFalse; - } - edge_view=DestroyCacheView(edge_view); - /* - Estimate hysteresis threshold. - */ - lower_threshold=lower_percent*(max-min)+min; - upper_threshold=upper_percent*(max-min)+min; - /* - Hysteresis threshold. - */ - edge_view=AcquireAuthenticCacheView(edge_image,exception); - for (y=0; y < (ssize_t) edge_image->rows; y++) - { - register ssize_t - x; - - if (status == MagickFalse) - continue; - for (x=0; x < (ssize_t) edge_image->columns; x++) - { - CannyInfo - pixel; - - register const Quantum - *magick_restrict p; - - /* - Edge if pixel gradient higher than upper threshold. - */ - p=GetCacheViewVirtualPixels(edge_view,x,y,1,1,exception); - if (p == (const Quantum *) NULL) - continue; - status=GetMatrixElement(canny_cache,x,y,&pixel); - if (status == MagickFalse) - continue; - if ((GetPixelIntensity(edge_image,p) == 0.0) && - (pixel.intensity >= upper_threshold)) - status=TraceEdges(edge_image,edge_view,canny_cache,x,y,lower_threshold, - exception); - } - if (image->progress_monitor != (MagickProgressMonitor) NULL) - { - MagickBooleanType - proceed; - -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp atomic -#endif - progress++; - proceed=SetImageProgress(image,CannyEdgeImageTag,progress,image->rows); - if (proceed == MagickFalse) - status=MagickFalse; - } - } - edge_view=DestroyCacheView(edge_view); - /* - Free resources. - */ - canny_cache=DestroyMatrixInfo(canny_cache); - return(edge_image); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % G e t I m a g e F e a t u r e s % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % GetImageFeatures() returns features for each channel in the image in - % each of four directions (horizontal, vertical, left and right diagonals) - % for the specified distance. The features include the angular second - % moment, contrast, correlation, sum of squares: variance, inverse difference - % moment, sum average, sum varience, sum entropy, entropy, difference variance,% difference entropy, information measures of correlation 1, information - % measures of correlation 2, and maximum correlation coefficient. You can - % access the red channel contrast, for example, like this: - % - % channel_features=GetImageFeatures(image,1,exception); - % contrast=channel_features[RedPixelChannel].contrast[0]; - % - % Use MagickRelinquishMemory() to free the features buffer. - % - % The format of the GetImageFeatures method is: - % - % ChannelFeatures *GetImageFeatures(const Image *image, - % const size_t distance,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image. - % - % o distance: the distance. - % - % o exception: return any errors or warnings in this structure. - % - */ - -static inline double MagickLog10(const double x) -{ -#define Log10Epsilon (1.0e-11) - - if (fabs(x) < Log10Epsilon) - return(log10(Log10Epsilon)); - return(log10(fabs(x))); -} - -MagickExport ChannelFeatures *GetImageFeatures(const Image *image, - const size_t distance,ExceptionInfo *exception) -{ - typedef struct _ChannelStatistics - { - PixelInfo - direction[4]; /* horizontal, vertical, left and right diagonals */ - } ChannelStatistics; - - CacheView - *image_view; - - ChannelFeatures - *channel_features; - - ChannelStatistics - **cooccurrence, - correlation, - *density_x, - *density_xy, - *density_y, - entropy_x, - entropy_xy, - entropy_xy1, - entropy_xy2, - entropy_y, - mean, - **Q, - *sum, - sum_squares, - variance; - - PixelPacket - gray, - *grays; - - MagickBooleanType - status; - - register ssize_t - i, - r; - - size_t - length; - - unsigned int - number_grays; - - assert(image != (Image *) NULL); - assert(image->signature == MagickCoreSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - if ((image->columns < (distance+1)) || (image->rows < (distance+1))) - return((ChannelFeatures *) NULL); - length=MaxPixelChannels+1UL; - channel_features=(ChannelFeatures *) AcquireQuantumMemory(length, - sizeof(*channel_features)); - if (channel_features == (ChannelFeatures *) NULL) - ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); - (void) memset(channel_features,0,length* - sizeof(*channel_features)); - /* - Form grays. - */ - grays=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays)); - if (grays == (PixelPacket *) NULL) - { - channel_features=(ChannelFeatures *) RelinquishMagickMemory( - channel_features); - (void) ThrowMagickException(exception,GetMagickModule(), - ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); - return(channel_features); - } - for (i=0; i <= (ssize_t) MaxMap; i++) - { - grays[i].red=(~0U); - grays[i].green=(~0U); - grays[i].blue=(~0U); - grays[i].alpha=(~0U); - grays[i].black=(~0U); - } - status=MagickTrue; - image_view=AcquireVirtualCacheView(image,exception); -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static) shared(status) \ - magick_number_threads(image,image,image->rows,1) -#endif - for (r=0; r < (ssize_t) image->rows; r++) - { - register const Quantum - *magick_restrict p; - - register ssize_t - x; - - if (status == MagickFalse) - continue; - p=GetCacheViewVirtualPixels(image_view,0,r,image->columns,1,exception); - if (p == (const Quantum *) NULL) - { - status=MagickFalse; - continue; - } - for (x=0; x < (ssize_t) image->columns; x++) - { - grays[ScaleQuantumToMap(GetPixelRed(image,p))].red= - ScaleQuantumToMap(GetPixelRed(image,p)); - grays[ScaleQuantumToMap(GetPixelGreen(image,p))].green= - ScaleQuantumToMap(GetPixelGreen(image,p)); - grays[ScaleQuantumToMap(GetPixelBlue(image,p))].blue= - ScaleQuantumToMap(GetPixelBlue(image,p)); - if (image->colorspace == CMYKColorspace) - grays[ScaleQuantumToMap(GetPixelBlack(image,p))].black= - ScaleQuantumToMap(GetPixelBlack(image,p)); - if (image->alpha_trait != UndefinedPixelTrait) - grays[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha= - ScaleQuantumToMap(GetPixelAlpha(image,p)); - p+=GetPixelChannels(image); - } - } - image_view=DestroyCacheView(image_view); - if (status == MagickFalse) - { - grays=(PixelPacket *) RelinquishMagickMemory(grays); - channel_features=(ChannelFeatures *) RelinquishMagickMemory( - channel_features); - return(channel_features); - } - (void) memset(&gray,0,sizeof(gray)); - for (i=0; i <= (ssize_t) MaxMap; i++) - { - if (grays[i].red != ~0U) - grays[gray.red++].red=grays[i].red; - if (grays[i].green != ~0U) - grays[gray.green++].green=grays[i].green; - if (grays[i].blue != ~0U) - grays[gray.blue++].blue=grays[i].blue; - if (image->colorspace == CMYKColorspace) - if (grays[i].black != ~0U) - grays[gray.black++].black=grays[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - if (grays[i].alpha != ~0U) - grays[gray.alpha++].alpha=grays[i].alpha; - } - /* - Allocate spatial dependence matrix. - */ - number_grays=gray.red; - if (gray.green > number_grays) - number_grays=gray.green; - if (gray.blue > number_grays) - number_grays=gray.blue; - if (image->colorspace == CMYKColorspace) - if (gray.black > number_grays) - number_grays=gray.black; - if (image->alpha_trait != UndefinedPixelTrait) - if (gray.alpha > number_grays) - number_grays=gray.alpha; - cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays, - sizeof(*cooccurrence)); - density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), - sizeof(*density_x)); - density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), - sizeof(*density_xy)); - density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), - sizeof(*density_y)); - Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q)); - sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum)); - if ((cooccurrence == (ChannelStatistics **) NULL) || - (density_x == (ChannelStatistics *) NULL) || - (density_xy == (ChannelStatistics *) NULL) || - (density_y == (ChannelStatistics *) NULL) || - (Q == (ChannelStatistics **) NULL) || - (sum == (ChannelStatistics *) NULL)) - { - if (Q != (ChannelStatistics **) NULL) - { - for (i=0; i < (ssize_t) number_grays; i++) - Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]); - Q=(ChannelStatistics **) RelinquishMagickMemory(Q); - } - if (sum != (ChannelStatistics *) NULL) - sum=(ChannelStatistics *) RelinquishMagickMemory(sum); - if (density_y != (ChannelStatistics *) NULL) - density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y); - if (density_xy != (ChannelStatistics *) NULL) - density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy); - if (density_x != (ChannelStatistics *) NULL) - density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x); - if (cooccurrence != (ChannelStatistics **) NULL) - { - for (i=0; i < (ssize_t) number_grays; i++) - cooccurrence[i]=(ChannelStatistics *) - RelinquishMagickMemory(cooccurrence[i]); - cooccurrence=(ChannelStatistics **) RelinquishMagickMemory( - cooccurrence); - } - grays=(PixelPacket *) RelinquishMagickMemory(grays); - channel_features=(ChannelFeatures *) RelinquishMagickMemory( - channel_features); - (void) ThrowMagickException(exception,GetMagickModule(), - ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); - return(channel_features); - } - (void) memset(&correlation,0,sizeof(correlation)); - (void) memset(density_x,0,2*(number_grays+1)*sizeof(*density_x)); - (void) memset(density_xy,0,2*(number_grays+1)*sizeof(*density_xy)); - (void) memset(density_y,0,2*(number_grays+1)*sizeof(*density_y)); - (void) memset(&mean,0,sizeof(mean)); - (void) memset(sum,0,number_grays*sizeof(*sum)); - (void) memset(&sum_squares,0,sizeof(sum_squares)); - (void) memset(density_xy,0,2*number_grays*sizeof(*density_xy)); - (void) memset(&entropy_x,0,sizeof(entropy_x)); - (void) memset(&entropy_xy,0,sizeof(entropy_xy)); - (void) memset(&entropy_xy1,0,sizeof(entropy_xy1)); - (void) memset(&entropy_xy2,0,sizeof(entropy_xy2)); - (void) memset(&entropy_y,0,sizeof(entropy_y)); - (void) memset(&variance,0,sizeof(variance)); - for (i=0; i < (ssize_t) number_grays; i++) - { - cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays, - sizeof(**cooccurrence)); - Q[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(**Q)); - if ((cooccurrence[i] == (ChannelStatistics *) NULL) || - (Q[i] == (ChannelStatistics *) NULL)) - break; - (void) memset(cooccurrence[i],0,number_grays* - sizeof(**cooccurrence)); - (void) memset(Q[i],0,number_grays*sizeof(**Q)); - } - if (i < (ssize_t) number_grays) - { - for (i--; i >= 0; i--) - { - if (Q[i] != (ChannelStatistics *) NULL) - Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]); - if (cooccurrence[i] != (ChannelStatistics *) NULL) - cooccurrence[i]=(ChannelStatistics *) - RelinquishMagickMemory(cooccurrence[i]); - } - Q=(ChannelStatistics **) RelinquishMagickMemory(Q); - cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence); - sum=(ChannelStatistics *) RelinquishMagickMemory(sum); - density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y); - density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy); - density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x); - grays=(PixelPacket *) RelinquishMagickMemory(grays); - channel_features=(ChannelFeatures *) RelinquishMagickMemory( - channel_features); - (void) ThrowMagickException(exception,GetMagickModule(), - ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); - return(channel_features); - } - /* - Initialize spatial dependence matrix. - */ - status=MagickTrue; - image_view=AcquireVirtualCacheView(image,exception); - for (r=0; r < (ssize_t) image->rows; r++) - { - register const Quantum - *magick_restrict p; - - register ssize_t - x; - - ssize_t - offset, - u, - v; - - if (status == MagickFalse) - continue; - p=GetCacheViewVirtualPixels(image_view,-(ssize_t) distance,r,image->columns+ - 2*distance,distance+2,exception); - if (p == (const Quantum *) NULL) - { - status=MagickFalse; - continue; - } - p+=distance*GetPixelChannels(image);; - for (x=0; x < (ssize_t) image->columns; x++) - { - for (i=0; i < 4; i++) - { - switch (i) - { - case 0: - default: - { - /* - Horizontal adjacency. - */ - offset=(ssize_t) distance; - break; - } - case 1: - { - /* - Vertical adjacency. - */ - offset=(ssize_t) (image->columns+2*distance); - break; - } - case 2: - { - /* - Right diagonal adjacency. - */ - offset=(ssize_t) ((image->columns+2*distance)-distance); - break; - } - case 3: - { - /* - Left diagonal adjacency. - */ - offset=(ssize_t) ((image->columns+2*distance)+distance); - break; - } - } - u=0; - v=0; - while (grays[u].red != ScaleQuantumToMap(GetPixelRed(image,p))) - u++; - while (grays[v].red != ScaleQuantumToMap(GetPixelRed(image,p+offset*GetPixelChannels(image)))) - v++; - cooccurrence[u][v].direction[i].red++; - cooccurrence[v][u].direction[i].red++; - u=0; - v=0; - while (grays[u].green != ScaleQuantumToMap(GetPixelGreen(image,p))) - u++; - while (grays[v].green != ScaleQuantumToMap(GetPixelGreen(image,p+offset*GetPixelChannels(image)))) - v++; - cooccurrence[u][v].direction[i].green++; - cooccurrence[v][u].direction[i].green++; - u=0; - v=0; - while (grays[u].blue != ScaleQuantumToMap(GetPixelBlue(image,p))) - u++; - while (grays[v].blue != ScaleQuantumToMap(GetPixelBlue(image,p+offset*GetPixelChannels(image)))) - v++; - cooccurrence[u][v].direction[i].blue++; - cooccurrence[v][u].direction[i].blue++; - if (image->colorspace == CMYKColorspace) - { - u=0; - v=0; - while (grays[u].black != ScaleQuantumToMap(GetPixelBlack(image,p))) - u++; - while (grays[v].black != ScaleQuantumToMap(GetPixelBlack(image,p+offset*GetPixelChannels(image)))) - v++; - cooccurrence[u][v].direction[i].black++; - cooccurrence[v][u].direction[i].black++; - } - if (image->alpha_trait != UndefinedPixelTrait) - { - u=0; - v=0; - while (grays[u].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p))) - u++; - while (grays[v].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p+offset*GetPixelChannels(image)))) - v++; - cooccurrence[u][v].direction[i].alpha++; - cooccurrence[v][u].direction[i].alpha++; - } - } - p+=GetPixelChannels(image); - } - } - grays=(PixelPacket *) RelinquishMagickMemory(grays); - image_view=DestroyCacheView(image_view); - if (status == MagickFalse) - { - for (i=0; i < (ssize_t) number_grays; i++) - cooccurrence[i]=(ChannelStatistics *) - RelinquishMagickMemory(cooccurrence[i]); - cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence); - channel_features=(ChannelFeatures *) RelinquishMagickMemory( - channel_features); - (void) ThrowMagickException(exception,GetMagickModule(), - ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); - return(channel_features); - } - /* - Normalize spatial dependence matrix. - */ - for (i=0; i < 4; i++) - { - double - normalize; - - register ssize_t - y; - - switch (i) - { - case 0: - default: - { - /* - Horizontal adjacency. - */ - normalize=2.0*image->rows*(image->columns-distance); - break; - } - case 1: - { - /* - Vertical adjacency. - */ - normalize=2.0*(image->rows-distance)*image->columns; - break; - } - case 2: - { - /* - Right diagonal adjacency. - */ - normalize=2.0*(image->rows-distance)*(image->columns-distance); - break; - } - case 3: - { - /* - Left diagonal adjacency. - */ - normalize=2.0*(image->rows-distance)*(image->columns-distance); - break; - } - } - normalize=PerceptibleReciprocal(normalize); - for (y=0; y < (ssize_t) number_grays; y++) - { - register ssize_t - x; - - for (x=0; x < (ssize_t) number_grays; x++) - { - cooccurrence[x][y].direction[i].red*=normalize; - cooccurrence[x][y].direction[i].green*=normalize; - cooccurrence[x][y].direction[i].blue*=normalize; - if (image->colorspace == CMYKColorspace) - cooccurrence[x][y].direction[i].black*=normalize; - if (image->alpha_trait != UndefinedPixelTrait) - cooccurrence[x][y].direction[i].alpha*=normalize; - } - } - } - /* - Compute texture features. - */ -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static) shared(status) \ - magick_number_threads(image,image,number_grays,1) -#endif - for (i=0; i < 4; i++) - { - register ssize_t - y; - - for (y=0; y < (ssize_t) number_grays; y++) - { - register ssize_t - x; - - for (x=0; x < (ssize_t) number_grays; x++) - { - /* - Angular second moment: measure of homogeneity of the image. - */ - channel_features[RedPixelChannel].angular_second_moment[i]+= - cooccurrence[x][y].direction[i].red* - cooccurrence[x][y].direction[i].red; - channel_features[GreenPixelChannel].angular_second_moment[i]+= - cooccurrence[x][y].direction[i].green* - cooccurrence[x][y].direction[i].green; - channel_features[BluePixelChannel].angular_second_moment[i]+= - cooccurrence[x][y].direction[i].blue* - cooccurrence[x][y].direction[i].blue; - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].angular_second_moment[i]+= - cooccurrence[x][y].direction[i].black* - cooccurrence[x][y].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].angular_second_moment[i]+= - cooccurrence[x][y].direction[i].alpha* - cooccurrence[x][y].direction[i].alpha; - /* - Correlation: measure of linear-dependencies in the image. - */ - sum[y].direction[i].red+=cooccurrence[x][y].direction[i].red; - sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green; - sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue; - if (image->colorspace == CMYKColorspace) - sum[y].direction[i].black+=cooccurrence[x][y].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - sum[y].direction[i].alpha+=cooccurrence[x][y].direction[i].alpha; - correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red; - correlation.direction[i].green+=x*y* - cooccurrence[x][y].direction[i].green; - correlation.direction[i].blue+=x*y* - cooccurrence[x][y].direction[i].blue; - if (image->colorspace == CMYKColorspace) - correlation.direction[i].black+=x*y* - cooccurrence[x][y].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - correlation.direction[i].alpha+=x*y* - cooccurrence[x][y].direction[i].alpha; - /* - Inverse Difference Moment. - */ - channel_features[RedPixelChannel].inverse_difference_moment[i]+= - cooccurrence[x][y].direction[i].red/((y-x)*(y-x)+1); - channel_features[GreenPixelChannel].inverse_difference_moment[i]+= - cooccurrence[x][y].direction[i].green/((y-x)*(y-x)+1); - channel_features[BluePixelChannel].inverse_difference_moment[i]+= - cooccurrence[x][y].direction[i].blue/((y-x)*(y-x)+1); - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].inverse_difference_moment[i]+= - cooccurrence[x][y].direction[i].black/((y-x)*(y-x)+1); - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].inverse_difference_moment[i]+= - cooccurrence[x][y].direction[i].alpha/((y-x)*(y-x)+1); - /* - Sum average. - */ - density_xy[y+x+2].direction[i].red+= - cooccurrence[x][y].direction[i].red; - density_xy[y+x+2].direction[i].green+= - cooccurrence[x][y].direction[i].green; - density_xy[y+x+2].direction[i].blue+= - cooccurrence[x][y].direction[i].blue; - if (image->colorspace == CMYKColorspace) - density_xy[y+x+2].direction[i].black+= - cooccurrence[x][y].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - density_xy[y+x+2].direction[i].alpha+= - cooccurrence[x][y].direction[i].alpha; - /* - Entropy. - */ - channel_features[RedPixelChannel].entropy[i]-= - cooccurrence[x][y].direction[i].red* - MagickLog10(cooccurrence[x][y].direction[i].red); - channel_features[GreenPixelChannel].entropy[i]-= - cooccurrence[x][y].direction[i].green* - MagickLog10(cooccurrence[x][y].direction[i].green); - channel_features[BluePixelChannel].entropy[i]-= - cooccurrence[x][y].direction[i].blue* - MagickLog10(cooccurrence[x][y].direction[i].blue); - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].entropy[i]-= - cooccurrence[x][y].direction[i].black* - MagickLog10(cooccurrence[x][y].direction[i].black); - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].entropy[i]-= - cooccurrence[x][y].direction[i].alpha* - MagickLog10(cooccurrence[x][y].direction[i].alpha); - /* - Information Measures of Correlation. - */ - density_x[x].direction[i].red+=cooccurrence[x][y].direction[i].red; - density_x[x].direction[i].green+=cooccurrence[x][y].direction[i].green; - density_x[x].direction[i].blue+=cooccurrence[x][y].direction[i].blue; - if (image->alpha_trait != UndefinedPixelTrait) - density_x[x].direction[i].alpha+= - cooccurrence[x][y].direction[i].alpha; - if (image->colorspace == CMYKColorspace) - density_x[x].direction[i].black+= - cooccurrence[x][y].direction[i].black; - density_y[y].direction[i].red+=cooccurrence[x][y].direction[i].red; - density_y[y].direction[i].green+=cooccurrence[x][y].direction[i].green; - density_y[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue; - if (image->colorspace == CMYKColorspace) - density_y[y].direction[i].black+= - cooccurrence[x][y].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - density_y[y].direction[i].alpha+= - cooccurrence[x][y].direction[i].alpha; - } - mean.direction[i].red+=y*sum[y].direction[i].red; - sum_squares.direction[i].red+=y*y*sum[y].direction[i].red; - mean.direction[i].green+=y*sum[y].direction[i].green; - sum_squares.direction[i].green+=y*y*sum[y].direction[i].green; - mean.direction[i].blue+=y*sum[y].direction[i].blue; - sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue; - if (image->colorspace == CMYKColorspace) - { - mean.direction[i].black+=y*sum[y].direction[i].black; - sum_squares.direction[i].black+=y*y*sum[y].direction[i].black; - } - if (image->alpha_trait != UndefinedPixelTrait) - { - mean.direction[i].alpha+=y*sum[y].direction[i].alpha; - sum_squares.direction[i].alpha+=y*y*sum[y].direction[i].alpha; - } - } - /* - Correlation: measure of linear-dependencies in the image. - */ - channel_features[RedPixelChannel].correlation[i]= - (correlation.direction[i].red-mean.direction[i].red* - mean.direction[i].red)/(sqrt(sum_squares.direction[i].red- - (mean.direction[i].red*mean.direction[i].red))*sqrt( - sum_squares.direction[i].red-(mean.direction[i].red* - mean.direction[i].red))); - channel_features[GreenPixelChannel].correlation[i]= - (correlation.direction[i].green-mean.direction[i].green* - mean.direction[i].green)/(sqrt(sum_squares.direction[i].green- - (mean.direction[i].green*mean.direction[i].green))*sqrt( - sum_squares.direction[i].green-(mean.direction[i].green* - mean.direction[i].green))); - channel_features[BluePixelChannel].correlation[i]= - (correlation.direction[i].blue-mean.direction[i].blue* - mean.direction[i].blue)/(sqrt(sum_squares.direction[i].blue- - (mean.direction[i].blue*mean.direction[i].blue))*sqrt( - sum_squares.direction[i].blue-(mean.direction[i].blue* - mean.direction[i].blue))); - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].correlation[i]= - (correlation.direction[i].black-mean.direction[i].black* - mean.direction[i].black)/(sqrt(sum_squares.direction[i].black- - (mean.direction[i].black*mean.direction[i].black))*sqrt( - sum_squares.direction[i].black-(mean.direction[i].black* - mean.direction[i].black))); - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].correlation[i]= - (correlation.direction[i].alpha-mean.direction[i].alpha* - mean.direction[i].alpha)/(sqrt(sum_squares.direction[i].alpha- - (mean.direction[i].alpha*mean.direction[i].alpha))*sqrt( - sum_squares.direction[i].alpha-(mean.direction[i].alpha* - mean.direction[i].alpha))); - } - /* - Compute more texture features. - */ -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static) shared(status) \ - magick_number_threads(image,image,number_grays,1) -#endif - for (i=0; i < 4; i++) - { - register ssize_t - x; - - for (x=2; x < (ssize_t) (2*number_grays); x++) - { - /* - Sum average. - */ - channel_features[RedPixelChannel].sum_average[i]+= - x*density_xy[x].direction[i].red; - channel_features[GreenPixelChannel].sum_average[i]+= - x*density_xy[x].direction[i].green; - channel_features[BluePixelChannel].sum_average[i]+= - x*density_xy[x].direction[i].blue; - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].sum_average[i]+= - x*density_xy[x].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].sum_average[i]+= - x*density_xy[x].direction[i].alpha; - /* - Sum entropy. - */ - channel_features[RedPixelChannel].sum_entropy[i]-= - density_xy[x].direction[i].red* - MagickLog10(density_xy[x].direction[i].red); - channel_features[GreenPixelChannel].sum_entropy[i]-= - density_xy[x].direction[i].green* - MagickLog10(density_xy[x].direction[i].green); - channel_features[BluePixelChannel].sum_entropy[i]-= - density_xy[x].direction[i].blue* - MagickLog10(density_xy[x].direction[i].blue); - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].sum_entropy[i]-= - density_xy[x].direction[i].black* - MagickLog10(density_xy[x].direction[i].black); - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].sum_entropy[i]-= - density_xy[x].direction[i].alpha* - MagickLog10(density_xy[x].direction[i].alpha); - /* - Sum variance. - */ - channel_features[RedPixelChannel].sum_variance[i]+= - (x-channel_features[RedPixelChannel].sum_entropy[i])* - (x-channel_features[RedPixelChannel].sum_entropy[i])* - density_xy[x].direction[i].red; - channel_features[GreenPixelChannel].sum_variance[i]+= - (x-channel_features[GreenPixelChannel].sum_entropy[i])* - (x-channel_features[GreenPixelChannel].sum_entropy[i])* - density_xy[x].direction[i].green; - channel_features[BluePixelChannel].sum_variance[i]+= - (x-channel_features[BluePixelChannel].sum_entropy[i])* - (x-channel_features[BluePixelChannel].sum_entropy[i])* - density_xy[x].direction[i].blue; - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].sum_variance[i]+= - (x-channel_features[BlackPixelChannel].sum_entropy[i])* - (x-channel_features[BlackPixelChannel].sum_entropy[i])* - density_xy[x].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].sum_variance[i]+= - (x-channel_features[AlphaPixelChannel].sum_entropy[i])* - (x-channel_features[AlphaPixelChannel].sum_entropy[i])* - density_xy[x].direction[i].alpha; - } - } - /* - Compute more texture features. - */ -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static) shared(status) \ - magick_number_threads(image,image,number_grays,1) -#endif - for (i=0; i < 4; i++) - { - register ssize_t - y; - - for (y=0; y < (ssize_t) number_grays; y++) - { - register ssize_t - x; - - for (x=0; x < (ssize_t) number_grays; x++) - { - /* - Sum of Squares: Variance - */ - variance.direction[i].red+=(y-mean.direction[i].red+1)* - (y-mean.direction[i].red+1)*cooccurrence[x][y].direction[i].red; - variance.direction[i].green+=(y-mean.direction[i].green+1)* - (y-mean.direction[i].green+1)*cooccurrence[x][y].direction[i].green; - variance.direction[i].blue+=(y-mean.direction[i].blue+1)* - (y-mean.direction[i].blue+1)*cooccurrence[x][y].direction[i].blue; - if (image->colorspace == CMYKColorspace) - variance.direction[i].black+=(y-mean.direction[i].black+1)* - (y-mean.direction[i].black+1)*cooccurrence[x][y].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - variance.direction[i].alpha+=(y-mean.direction[i].alpha+1)* - (y-mean.direction[i].alpha+1)* - cooccurrence[x][y].direction[i].alpha; - /* - Sum average / Difference Variance. - */ - density_xy[MagickAbsoluteValue(y-x)].direction[i].red+= - cooccurrence[x][y].direction[i].red; - density_xy[MagickAbsoluteValue(y-x)].direction[i].green+= - cooccurrence[x][y].direction[i].green; - density_xy[MagickAbsoluteValue(y-x)].direction[i].blue+= - cooccurrence[x][y].direction[i].blue; - if (image->colorspace == CMYKColorspace) - density_xy[MagickAbsoluteValue(y-x)].direction[i].black+= - cooccurrence[x][y].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - density_xy[MagickAbsoluteValue(y-x)].direction[i].alpha+= - cooccurrence[x][y].direction[i].alpha; - /* - Information Measures of Correlation. - */ - entropy_xy.direction[i].red-=cooccurrence[x][y].direction[i].red* - MagickLog10(cooccurrence[x][y].direction[i].red); - entropy_xy.direction[i].green-=cooccurrence[x][y].direction[i].green* - MagickLog10(cooccurrence[x][y].direction[i].green); - entropy_xy.direction[i].blue-=cooccurrence[x][y].direction[i].blue* - MagickLog10(cooccurrence[x][y].direction[i].blue); - if (image->colorspace == CMYKColorspace) - entropy_xy.direction[i].black-=cooccurrence[x][y].direction[i].black* - MagickLog10(cooccurrence[x][y].direction[i].black); - if (image->alpha_trait != UndefinedPixelTrait) - entropy_xy.direction[i].alpha-= - cooccurrence[x][y].direction[i].alpha*MagickLog10( - cooccurrence[x][y].direction[i].alpha); - entropy_xy1.direction[i].red-=(cooccurrence[x][y].direction[i].red* - MagickLog10(density_x[x].direction[i].red*density_y[y].direction[i].red)); - entropy_xy1.direction[i].green-=(cooccurrence[x][y].direction[i].green* - MagickLog10(density_x[x].direction[i].green* - density_y[y].direction[i].green)); - entropy_xy1.direction[i].blue-=(cooccurrence[x][y].direction[i].blue* - MagickLog10(density_x[x].direction[i].blue*density_y[y].direction[i].blue)); - if (image->colorspace == CMYKColorspace) - entropy_xy1.direction[i].black-=( - cooccurrence[x][y].direction[i].black*MagickLog10( - density_x[x].direction[i].black*density_y[y].direction[i].black)); - if (image->alpha_trait != UndefinedPixelTrait) - entropy_xy1.direction[i].alpha-=( - cooccurrence[x][y].direction[i].alpha*MagickLog10( - density_x[x].direction[i].alpha*density_y[y].direction[i].alpha)); - entropy_xy2.direction[i].red-=(density_x[x].direction[i].red* - density_y[y].direction[i].red*MagickLog10(density_x[x].direction[i].red* - density_y[y].direction[i].red)); - entropy_xy2.direction[i].green-=(density_x[x].direction[i].green* - density_y[y].direction[i].green*MagickLog10(density_x[x].direction[i].green* - density_y[y].direction[i].green)); - entropy_xy2.direction[i].blue-=(density_x[x].direction[i].blue* - density_y[y].direction[i].blue*MagickLog10(density_x[x].direction[i].blue* - density_y[y].direction[i].blue)); - if (image->colorspace == CMYKColorspace) - entropy_xy2.direction[i].black-=(density_x[x].direction[i].black* - density_y[y].direction[i].black*MagickLog10( - density_x[x].direction[i].black*density_y[y].direction[i].black)); - if (image->alpha_trait != UndefinedPixelTrait) - entropy_xy2.direction[i].alpha-=(density_x[x].direction[i].alpha* - density_y[y].direction[i].alpha*MagickLog10( - density_x[x].direction[i].alpha*density_y[y].direction[i].alpha)); - } - } - channel_features[RedPixelChannel].variance_sum_of_squares[i]= - variance.direction[i].red; - channel_features[GreenPixelChannel].variance_sum_of_squares[i]= - variance.direction[i].green; - channel_features[BluePixelChannel].variance_sum_of_squares[i]= - variance.direction[i].blue; - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].variance_sum_of_squares[i]= - variance.direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].variance_sum_of_squares[i]= - variance.direction[i].alpha; - } - /* - Compute more texture features. - */ - (void) memset(&variance,0,sizeof(variance)); - (void) memset(&sum_squares,0,sizeof(sum_squares)); -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static) shared(status) \ - magick_number_threads(image,image,number_grays,1) -#endif - for (i=0; i < 4; i++) - { - register ssize_t - x; - - for (x=0; x < (ssize_t) number_grays; x++) - { - /* - Difference variance. - */ - variance.direction[i].red+=density_xy[x].direction[i].red; - variance.direction[i].green+=density_xy[x].direction[i].green; - variance.direction[i].blue+=density_xy[x].direction[i].blue; - if (image->colorspace == CMYKColorspace) - variance.direction[i].black+=density_xy[x].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - variance.direction[i].alpha+=density_xy[x].direction[i].alpha; - sum_squares.direction[i].red+=density_xy[x].direction[i].red* - density_xy[x].direction[i].red; - sum_squares.direction[i].green+=density_xy[x].direction[i].green* - density_xy[x].direction[i].green; - sum_squares.direction[i].blue+=density_xy[x].direction[i].blue* - density_xy[x].direction[i].blue; - if (image->colorspace == CMYKColorspace) - sum_squares.direction[i].black+=density_xy[x].direction[i].black* - density_xy[x].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - sum_squares.direction[i].alpha+=density_xy[x].direction[i].alpha* - density_xy[x].direction[i].alpha; - /* - Difference entropy. - */ - channel_features[RedPixelChannel].difference_entropy[i]-= - density_xy[x].direction[i].red* - MagickLog10(density_xy[x].direction[i].red); - channel_features[GreenPixelChannel].difference_entropy[i]-= - density_xy[x].direction[i].green* - MagickLog10(density_xy[x].direction[i].green); - channel_features[BluePixelChannel].difference_entropy[i]-= - density_xy[x].direction[i].blue* - MagickLog10(density_xy[x].direction[i].blue); - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].difference_entropy[i]-= - density_xy[x].direction[i].black* - MagickLog10(density_xy[x].direction[i].black); - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].difference_entropy[i]-= - density_xy[x].direction[i].alpha* - MagickLog10(density_xy[x].direction[i].alpha); - /* - Information Measures of Correlation. - */ - entropy_x.direction[i].red-=(density_x[x].direction[i].red* - MagickLog10(density_x[x].direction[i].red)); - entropy_x.direction[i].green-=(density_x[x].direction[i].green* - MagickLog10(density_x[x].direction[i].green)); - entropy_x.direction[i].blue-=(density_x[x].direction[i].blue* - MagickLog10(density_x[x].direction[i].blue)); - if (image->colorspace == CMYKColorspace) - entropy_x.direction[i].black-=(density_x[x].direction[i].black* - MagickLog10(density_x[x].direction[i].black)); - if (image->alpha_trait != UndefinedPixelTrait) - entropy_x.direction[i].alpha-=(density_x[x].direction[i].alpha* - MagickLog10(density_x[x].direction[i].alpha)); - entropy_y.direction[i].red-=(density_y[x].direction[i].red* - MagickLog10(density_y[x].direction[i].red)); - entropy_y.direction[i].green-=(density_y[x].direction[i].green* - MagickLog10(density_y[x].direction[i].green)); - entropy_y.direction[i].blue-=(density_y[x].direction[i].blue* - MagickLog10(density_y[x].direction[i].blue)); - if (image->colorspace == CMYKColorspace) - entropy_y.direction[i].black-=(density_y[x].direction[i].black* - MagickLog10(density_y[x].direction[i].black)); - if (image->alpha_trait != UndefinedPixelTrait) - entropy_y.direction[i].alpha-=(density_y[x].direction[i].alpha* - MagickLog10(density_y[x].direction[i].alpha)); - } - /* - Difference variance. - */ - channel_features[RedPixelChannel].difference_variance[i]= - (((double) number_grays*number_grays*sum_squares.direction[i].red)- - (variance.direction[i].red*variance.direction[i].red))/ - ((double) number_grays*number_grays*number_grays*number_grays); - channel_features[GreenPixelChannel].difference_variance[i]= - (((double) number_grays*number_grays*sum_squares.direction[i].green)- - (variance.direction[i].green*variance.direction[i].green))/ - ((double) number_grays*number_grays*number_grays*number_grays); - channel_features[BluePixelChannel].difference_variance[i]= - (((double) number_grays*number_grays*sum_squares.direction[i].blue)- - (variance.direction[i].blue*variance.direction[i].blue))/ - ((double) number_grays*number_grays*number_grays*number_grays); - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].difference_variance[i]= - (((double) number_grays*number_grays*sum_squares.direction[i].black)- - (variance.direction[i].black*variance.direction[i].black))/ - ((double) number_grays*number_grays*number_grays*number_grays); - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].difference_variance[i]= - (((double) number_grays*number_grays*sum_squares.direction[i].alpha)- - (variance.direction[i].alpha*variance.direction[i].alpha))/ - ((double) number_grays*number_grays*number_grays*number_grays); - /* - Information Measures of Correlation. - */ - channel_features[RedPixelChannel].measure_of_correlation_1[i]= - (entropy_xy.direction[i].red-entropy_xy1.direction[i].red)/ - (entropy_x.direction[i].red > entropy_y.direction[i].red ? - entropy_x.direction[i].red : entropy_y.direction[i].red); - channel_features[GreenPixelChannel].measure_of_correlation_1[i]= - (entropy_xy.direction[i].green-entropy_xy1.direction[i].green)/ - (entropy_x.direction[i].green > entropy_y.direction[i].green ? - entropy_x.direction[i].green : entropy_y.direction[i].green); - channel_features[BluePixelChannel].measure_of_correlation_1[i]= - (entropy_xy.direction[i].blue-entropy_xy1.direction[i].blue)/ - (entropy_x.direction[i].blue > entropy_y.direction[i].blue ? - entropy_x.direction[i].blue : entropy_y.direction[i].blue); - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].measure_of_correlation_1[i]= - (entropy_xy.direction[i].black-entropy_xy1.direction[i].black)/ - (entropy_x.direction[i].black > entropy_y.direction[i].black ? - entropy_x.direction[i].black : entropy_y.direction[i].black); - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].measure_of_correlation_1[i]= - (entropy_xy.direction[i].alpha-entropy_xy1.direction[i].alpha)/ - (entropy_x.direction[i].alpha > entropy_y.direction[i].alpha ? - entropy_x.direction[i].alpha : entropy_y.direction[i].alpha); - channel_features[RedPixelChannel].measure_of_correlation_2[i]= - (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].red- - entropy_xy.direction[i].red))))); - channel_features[GreenPixelChannel].measure_of_correlation_2[i]= - (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].green- - entropy_xy.direction[i].green))))); - channel_features[BluePixelChannel].measure_of_correlation_2[i]= - (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].blue- - entropy_xy.direction[i].blue))))); - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].measure_of_correlation_2[i]= - (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].black- - entropy_xy.direction[i].black))))); - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].measure_of_correlation_2[i]= - (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].alpha- - entropy_xy.direction[i].alpha))))); - } - /* - Compute more texture features. - */ -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static) shared(status) \ - magick_number_threads(image,image,number_grays,1) -#endif - for (i=0; i < 4; i++) - { - ssize_t - z; - - for (z=0; z < (ssize_t) number_grays; z++) - { - register ssize_t - y; - - ChannelStatistics - pixel; - - (void) memset(&pixel,0,sizeof(pixel)); - for (y=0; y < (ssize_t) number_grays; y++) - { - register ssize_t - x; - - for (x=0; x < (ssize_t) number_grays; x++) - { - /* - Contrast: amount of local variations present in an image. - */ - if (((y-x) == z) || ((x-y) == z)) - { - pixel.direction[i].red+=cooccurrence[x][y].direction[i].red; - pixel.direction[i].green+=cooccurrence[x][y].direction[i].green; - pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue; - if (image->colorspace == CMYKColorspace) - pixel.direction[i].black+=cooccurrence[x][y].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - pixel.direction[i].alpha+= - cooccurrence[x][y].direction[i].alpha; - } - /* - Maximum Correlation Coefficient. - */ - Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red* - cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/ - density_y[x].direction[i].red; - Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green* - cooccurrence[y][x].direction[i].green/ - density_x[z].direction[i].green/density_y[x].direction[i].red; - Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue* - cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/ - density_y[x].direction[i].blue; - if (image->colorspace == CMYKColorspace) - Q[z][y].direction[i].black+=cooccurrence[z][x].direction[i].black* - cooccurrence[y][x].direction[i].black/ - density_x[z].direction[i].black/density_y[x].direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - Q[z][y].direction[i].alpha+= - cooccurrence[z][x].direction[i].alpha* - cooccurrence[y][x].direction[i].alpha/ - density_x[z].direction[i].alpha/ - density_y[x].direction[i].alpha; - } - } - channel_features[RedPixelChannel].contrast[i]+=z*z* - pixel.direction[i].red; - channel_features[GreenPixelChannel].contrast[i]+=z*z* - pixel.direction[i].green; - channel_features[BluePixelChannel].contrast[i]+=z*z* - pixel.direction[i].blue; - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].contrast[i]+=z*z* - pixel.direction[i].black; - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].contrast[i]+=z*z* - pixel.direction[i].alpha; - } - /* - Maximum Correlation Coefficient. - Future: return second largest eigenvalue of Q. - */ - channel_features[RedPixelChannel].maximum_correlation_coefficient[i]= - sqrt((double) -1.0); - channel_features[GreenPixelChannel].maximum_correlation_coefficient[i]= - sqrt((double) -1.0); - channel_features[BluePixelChannel].maximum_correlation_coefficient[i]= - sqrt((double) -1.0); - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].maximum_correlation_coefficient[i]= - sqrt((double) -1.0); - if (image->alpha_trait != UndefinedPixelTrait) - channel_features[AlphaPixelChannel].maximum_correlation_coefficient[i]= - sqrt((double) -1.0); - } - /* - Relinquish resources. - */ - sum=(ChannelStatistics *) RelinquishMagickMemory(sum); - for (i=0; i < (ssize_t) number_grays; i++) - Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]); - Q=(ChannelStatistics **) RelinquishMagickMemory(Q); - density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y); - density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy); - density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x); - for (i=0; i < (ssize_t) number_grays; i++) - cooccurrence[i]=(ChannelStatistics *) - RelinquishMagickMemory(cooccurrence[i]); - cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence); - return(channel_features); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % H o u g h L i n e I m a g e % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % Use HoughLineImage() in conjunction with any binary edge extracted image (we - % recommand Canny) to identify lines in the image. The algorithm accumulates - % counts for every white pixel for every possible orientation (for angles from - % 0 to 179 in 1 degree increments) and distance from the center of the image to - % the corner (in 1 px increments) and stores the counts in an accumulator - % matrix of angle vs distance. The size of the accumulator is 180x(diagonal/2). - % Next it searches this space for peaks in counts and converts the locations - % of the peaks to slope and intercept in the normal x,y input image space. Use - % the slope/intercepts to find the endpoints clipped to the bounds of the - % image. The lines are then drawn. The counts are a measure of the length of - % the lines. - % - % The format of the HoughLineImage method is: - % - % Image *HoughLineImage(const Image *image,const size_t width, - % const size_t height,const size_t threshold,ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image. - % - % o width, height: find line pairs as local maxima in this neighborhood. - % - % o threshold: the line count threshold. - % - % o exception: return any errors or warnings in this structure. - % - */ - -static inline double MagickRound(double x) -{ - /* - Round the fraction to nearest integer. - */ - if ((x-floor(x)) < (ceil(x)-x)) - return(floor(x)); - return(ceil(x)); -} - -static Image *RenderHoughLines(const ImageInfo *image_info,const size_t columns, - const size_t rows,ExceptionInfo *exception) -{ -#define BoundingBox "viewbox" - - DrawInfo - *draw_info; - - Image - *image; - - MagickBooleanType - status; - - /* - Open image. - */ - image=AcquireImage(image_info,exception); - status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); - if (status == MagickFalse) - { - image=DestroyImageList(image); - return((Image *) NULL); - } - image->columns=columns; - image->rows=rows; - draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL); - draw_info->affine.sx=image->resolution.x == 0.0 ? 1.0 : image->resolution.x/ - DefaultResolution; - draw_info->affine.sy=image->resolution.y == 0.0 ? 1.0 : image->resolution.y/ - DefaultResolution; - image->columns=(size_t) (draw_info->affine.sx*image->columns); - image->rows=(size_t) (draw_info->affine.sy*image->rows); - status=SetImageExtent(image,image->columns,image->rows,exception); - if (status == MagickFalse) - return(DestroyImageList(image)); - if (SetImageBackgroundColor(image,exception) == MagickFalse) - { - image=DestroyImageList(image); - return((Image *) NULL); - } - /* - Render drawing. - */ - if (GetBlobStreamData(image) == (unsigned char *) NULL) - draw_info->primitive=FileToString(image->filename,~0UL,exception); - else - { - draw_info->primitive=(char *) AcquireMagickMemory((size_t) - GetBlobSize(image)+1); - if (draw_info->primitive != (char *) NULL) - { - (void) memcpy(draw_info->primitive,GetBlobStreamData(image), - (size_t) GetBlobSize(image)); - draw_info->primitive[GetBlobSize(image)]='\0'; - } - } - (void) DrawImage(image,draw_info,exception); - draw_info=DestroyDrawInfo(draw_info); - (void) CloseBlob(image); - return(GetFirstImageInList(image)); -} - -MagickExport Image *HoughLineImage(const Image *image,const size_t width, - const size_t height,const size_t threshold,ExceptionInfo *exception) -{ -#define HoughLineImageTag "HoughLine/Image" - - CacheView - *image_view; - - char - message[MagickPathExtent], - path[MagickPathExtent]; - - const char - *artifact; - - double - hough_height; - - Image - *lines_image = NULL; - - ImageInfo - *image_info; - - int - file; - - MagickBooleanType - status; - - MagickOffsetType - progress; - - MatrixInfo - *accumulator; - - PointInfo - center; - - register ssize_t - y; - - size_t - accumulator_height, - accumulator_width, - line_count; - - /* - Create the accumulator. - */ - assert(image != (const Image *) NULL); - assert(image->signature == MagickCoreSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - accumulator_width=180; - hough_height=((sqrt(2.0)*(double) (image->rows > image->columns ? - image->rows : image->columns))/2.0); - accumulator_height=(size_t) (2.0*hough_height); - accumulator=AcquireMatrixInfo(accumulator_width,accumulator_height, - sizeof(double),exception); - if (accumulator == (MatrixInfo *) NULL) - ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); - if (NullMatrix(accumulator) == MagickFalse) - { - accumulator=DestroyMatrixInfo(accumulator); - ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); - } - /* - Populate the accumulator. - */ - status=MagickTrue; - progress=0; - center.x=(double) image->columns/2.0; - center.y=(double) image->rows/2.0; - image_view=AcquireVirtualCacheView(image,exception); - for (y=0; y < (ssize_t) image->rows; y++) - { - register const Quantum - *magick_restrict p; - - register ssize_t - x; - - if (status == MagickFalse) - continue; - p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); - if (p == (Quantum *) NULL) - { - status=MagickFalse; - continue; - } - for (x=0; x < (ssize_t) image->columns; x++) - { - if (GetPixelIntensity(image,p) > (QuantumRange/2.0)) - { - register ssize_t - i; - - for (i=0; i < 180; i++) - { - double - count, - radius; - - radius=(((double) x-center.x)*cos(DegreesToRadians((double) i)))+ - (((double) y-center.y)*sin(DegreesToRadians((double) i))); - (void) GetMatrixElement(accumulator,i,(ssize_t) - MagickRound(radius+hough_height),&count); - count++; - (void) SetMatrixElement(accumulator,i,(ssize_t) - MagickRound(radius+hough_height),&count); - } - } - p+=GetPixelChannels(image); - } - if (image->progress_monitor != (MagickProgressMonitor) NULL) - { - MagickBooleanType - proceed; - -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp atomic -#endif - progress++; - proceed=SetImageProgress(image,CannyEdgeImageTag,progress,image->rows); - if (proceed == MagickFalse) - status=MagickFalse; - } - } - image_view=DestroyCacheView(image_view); - if (status == MagickFalse) - { - accumulator=DestroyMatrixInfo(accumulator); - return((Image *) NULL); - } - /* - Generate line segments from accumulator. - */ - file=AcquireUniqueFileResource(path); - if (file == -1) - { - accumulator=DestroyMatrixInfo(accumulator); - return((Image *) NULL); - } - (void) FormatLocaleString(message,MagickPathExtent, - "# Hough line transform: %.20gx%.20g%+.20g\n",(double) width, - (double) height,(double) threshold); - if (write(file,message,strlen(message)) != (ssize_t) strlen(message)) - status=MagickFalse; - (void) FormatLocaleString(message,MagickPathExtent, - "viewbox 0 0 %.20g %.20g\n",(double) image->columns,(double) image->rows); - if (write(file,message,strlen(message)) != (ssize_t) strlen(message)) - status=MagickFalse; - (void) FormatLocaleString(message,MagickPathExtent, - "# x1,y1 x2,y2 # count angle distance\n"); - if (write(file,message,strlen(message)) != (ssize_t) strlen(message)) - status=MagickFalse; - line_count=image->columns > image->rows ? image->columns/4 : image->rows/4; - if (threshold != 0) - line_count=threshold; - for (y=0; y < (ssize_t) accumulator_height; y++) - { - register ssize_t - x; - - for (x=0; x < (ssize_t) accumulator_width; x++) - { - double - count; - - (void) GetMatrixElement(accumulator,x,y,&count); - if (count >= (double) line_count) - { - double - maxima; - - SegmentInfo - line; - - ssize_t - v; - - /* - Is point a local maxima? - */ - maxima=count; - for (v=(-((ssize_t) height/2)); v <= (((ssize_t) height/2)); v++) - { - ssize_t - u; - - for (u=(-((ssize_t) width/2)); u <= (((ssize_t) width/2)); u++) - { - if ((u != 0) || (v !=0)) - { - (void) GetMatrixElement(accumulator,x+u,y+v,&count); - if (count > maxima) - { - maxima=count; - break; - } - } - } - if (u < (ssize_t) (width/2)) - break; - } - (void) GetMatrixElement(accumulator,x,y,&count); - if (maxima > count) - continue; - if ((x >= 45) && (x <= 135)) - { - /* - y = (r-x cos(t))/sin(t) - */ - line.x1=0.0; - line.y1=((double) (y-(accumulator_height/2.0))-((line.x1- - (image->columns/2.0))*cos(DegreesToRadians((double) x))))/ - sin(DegreesToRadians((double) x))+(image->rows/2.0); - line.x2=(double) image->columns; - line.y2=((double) (y-(accumulator_height/2.0))-((line.x2- - (image->columns/2.0))*cos(DegreesToRadians((double) x))))/ - sin(DegreesToRadians((double) x))+(image->rows/2.0); - } - else - { - /* - x = (r-y cos(t))/sin(t) - */ - line.y1=0.0; - line.x1=((double) (y-(accumulator_height/2.0))-((line.y1- - (image->rows/2.0))*sin(DegreesToRadians((double) x))))/ - cos(DegreesToRadians((double) x))+(image->columns/2.0); - line.y2=(double) image->rows; - line.x2=((double) (y-(accumulator_height/2.0))-((line.y2- - (image->rows/2.0))*sin(DegreesToRadians((double) x))))/ - cos(DegreesToRadians((double) x))+(image->columns/2.0); - } - (void) FormatLocaleString(message,MagickPathExtent, - "line %g,%g %g,%g # %g %g %g\n",line.x1,line.y1,line.x2,line.y2, - maxima,(double) x,(double) y); - if (write(file,message,strlen(message)) != (ssize_t) strlen(message)) - status=MagickFalse; - } - } - } - (void) close(file); - /* - Render lines to image canvas. - */ - image_info=AcquireImageInfo(); - image_info->background_color=image->background_color; - (void) FormatLocaleString(image_info->filename,MagickPathExtent,"%s",path); - artifact=GetImageArtifact(image,"background"); - if (artifact != (const char *) NULL) - (void) SetImageOption(image_info,"background",artifact); - artifact=GetImageArtifact(image,"fill"); - if (artifact != (const char *) NULL) - (void) SetImageOption(image_info,"fill",artifact); - artifact=GetImageArtifact(image,"stroke"); - if (artifact != (const char *) NULL) - (void) SetImageOption(image_info,"stroke",artifact); - artifact=GetImageArtifact(image,"strokewidth"); - if (artifact != (const char *) NULL) - (void) SetImageOption(image_info,"strokewidth",artifact); - lines_image=RenderHoughLines(image_info,image->columns,image->rows,exception); - artifact=GetImageArtifact(image,"hough-lines:accumulator"); - if ((lines_image != (Image *) NULL) && - (IsStringTrue(artifact) != MagickFalse)) - { - Image - *accumulator_image; - - accumulator_image=MatrixToImage(accumulator,exception); - if (accumulator_image != (Image *) NULL) - AppendImageToList(&lines_image,accumulator_image); - } - /* - Free resources. - */ - accumulator=DestroyMatrixInfo(accumulator); - image_info=DestroyImageInfo(image_info); - (void) RelinquishUniqueFileResource(path); - return(GetFirstImageInList(lines_image)); -} - -/* - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % % - % % - % % - % M e a n S h i f t I m a g e % - % % - % % - % % - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % - % MeanShiftImage() delineate arbitrarily shaped clusters in the image. For - % each pixel, it visits all the pixels in the neighborhood specified by - % the window centered at the pixel and excludes those that are outside the - % radius=(window-1)/2 surrounding the pixel. From those pixels, it finds those - % that are within the specified color distance from the current mean, and - % computes a new x,y centroid from those coordinates and a new mean. This new - % x,y centroid is used as the center for a new window. This process iterates - % until it converges and the final mean is replaces the (original window - % center) pixel value. It repeats this process for the next pixel, etc., - % until it processes all pixels in the image. Results are typically better with - % colorspaces other than sRGB. We recommend YIQ, YUV or YCbCr. - % - % The format of the MeanShiftImage method is: - % - % Image *MeanShiftImage(const Image *image,const size_t width, - % const size_t height,const double color_distance, - % ExceptionInfo *exception) - % - % A description of each parameter follows: - % - % o image: the image. - % - % o width, height: find pixels in this neighborhood. - % - % o color_distance: the color distance. - % - % o exception: return any errors or warnings in this structure. - % - */ -MagickExport Image *MeanShiftImage(const Image *image,const size_t width, - const size_t height,const double color_distance,ExceptionInfo *exception) -{ -#define MaxMeanShiftIterations 100 -#define MeanShiftImageTag "MeanShift/Image" - - CacheView - *image_view, - *mean_view, - *pixel_view; - - Image - *mean_image; - - MagickBooleanType - status; - - MagickOffsetType - progress; - - ssize_t - y; - - assert(image != (const Image *) NULL); - assert(image->signature == MagickCoreSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickCoreSignature); - mean_image=CloneImage(image,0,0,MagickTrue,exception); - if (mean_image == (Image *) NULL) - return((Image *) NULL); - if (SetImageStorageClass(mean_image,DirectClass,exception) == MagickFalse) - { - mean_image=DestroyImage(mean_image); - return((Image *) NULL); - } - status=MagickTrue; - progress=0; - image_view=AcquireVirtualCacheView(image,exception); - pixel_view=AcquireVirtualCacheView(image,exception); - mean_view=AcquireAuthenticCacheView(mean_image,exception); -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static) shared(status,progress) \ - magick_number_threads(mean_image,mean_image,mean_image->rows,1) -#endif - for (y=0; y < (ssize_t) mean_image->rows; y++) - { - register const Quantum - *magick_restrict p; - - register Quantum - *magick_restrict q; - - register ssize_t - x; - - if (status == MagickFalse) - continue; - p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); - q=GetCacheViewAuthenticPixels(mean_view,0,y,mean_image->columns,1, - exception); - if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) - { - status=MagickFalse; - continue; - } - for (x=0; x < (ssize_t) mean_image->columns; x++) - { - PixelInfo - mean_pixel, - previous_pixel; - - PointInfo - mean_location, - previous_location; - - register ssize_t - i; - - GetPixelInfo(image,&mean_pixel); - GetPixelInfoPixel(image,p,&mean_pixel); - mean_location.x=(double) x; - mean_location.y=(double) y; - for (i=0; i < MaxMeanShiftIterations; i++) - { - double - distance, - gamma; - - PixelInfo - sum_pixel; - - PointInfo - sum_location; - - ssize_t - count, - v; - - sum_location.x=0.0; - sum_location.y=0.0; - GetPixelInfo(image,&sum_pixel); - previous_location=mean_location; - previous_pixel=mean_pixel; - count=0; - for (v=(-((ssize_t) height/2)); v <= (((ssize_t) height/2)); v++) - { - ssize_t - u; - - for (u=(-((ssize_t) width/2)); u <= (((ssize_t) width/2)); u++) - { - if ((v*v+u*u) <= (ssize_t) ((width/2)*(height/2))) - { - PixelInfo - pixel; - - status=GetOneCacheViewVirtualPixelInfo(pixel_view,(ssize_t) - MagickRound(mean_location.x+u),(ssize_t) MagickRound( - mean_location.y+v),&pixel,exception); - distance=(mean_pixel.red-pixel.red)*(mean_pixel.red-pixel.red)+ - (mean_pixel.green-pixel.green)*(mean_pixel.green-pixel.green)+ - (mean_pixel.blue-pixel.blue)*(mean_pixel.blue-pixel.blue); - if (distance <= (color_distance*color_distance)) - { - sum_location.x+=mean_location.x+u; - sum_location.y+=mean_location.y+v; - sum_pixel.red+=pixel.red; - sum_pixel.green+=pixel.green; - sum_pixel.blue+=pixel.blue; - sum_pixel.alpha+=pixel.alpha; - count++; - } - } - } - } - gamma=1.0/count; - mean_location.x=gamma*sum_location.x; - mean_location.y=gamma*sum_location.y; - mean_pixel.red=gamma*sum_pixel.red; - mean_pixel.green=gamma*sum_pixel.green; - mean_pixel.blue=gamma*sum_pixel.blue; - mean_pixel.alpha=gamma*sum_pixel.alpha; - distance=(mean_location.x-previous_location.x)* - (mean_location.x-previous_location.x)+ - (mean_location.y-previous_location.y)* - (mean_location.y-previous_location.y)+ - 255.0*QuantumScale*(mean_pixel.red-previous_pixel.red)* - 255.0*QuantumScale*(mean_pixel.red-previous_pixel.red)+ - 255.0*QuantumScale*(mean_pixel.green-previous_pixel.green)* - 255.0*QuantumScale*(mean_pixel.green-previous_pixel.green)+ - 255.0*QuantumScale*(mean_pixel.blue-previous_pixel.blue)* - 255.0*QuantumScale*(mean_pixel.blue-previous_pixel.blue); - if (distance <= 3.0) - break; - } - SetPixelRed(mean_image,ClampToQuantum(mean_pixel.red),q); - SetPixelGreen(mean_image,ClampToQuantum(mean_pixel.green),q); - SetPixelBlue(mean_image,ClampToQuantum(mean_pixel.blue),q); - SetPixelAlpha(mean_image,ClampToQuantum(mean_pixel.alpha),q); - p+=GetPixelChannels(image); - q+=GetPixelChannels(mean_image); - } - if (SyncCacheViewAuthenticPixels(mean_view,exception) == MagickFalse) - status=MagickFalse; - if (image->progress_monitor != (MagickProgressMonitor) NULL) - { - MagickBooleanType - proceed; - -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp atomic -#endif - progress++; - proceed=SetImageProgress(image,MeanShiftImageTag,progress,image->rows); - if (proceed == MagickFalse) - status=MagickFalse; - } - } - mean_view=DestroyCacheView(mean_view); - pixel_view=DestroyCacheView(pixel_view); - image_view=DestroyCacheView(image_view); - return(mean_image); -} diff --git a/test/bug-hunting/cve/CVE-2019-15939/cmd.txt b/test/bug-hunting/cve/CVE-2019-15939/cmd.txt deleted file mode 100644 index a12bdbf06e0..00000000000 --- a/test/bug-hunting/cve/CVE-2019-15939/cmd.txt +++ /dev/null @@ -1,2 +0,0 @@ --DCV_EXPORTS_W= --DCV_EXPORTS= diff --git a/test/bug-hunting/cve/CVE-2019-15939/expected.txt b/test/bug-hunting/cve/CVE-2019-15939/expected.txt deleted file mode 100644 index c1966f235c1..00000000000 --- a/test/bug-hunting/cve/CVE-2019-15939/expected.txt +++ /dev/null @@ -1,5 +0,0 @@ -hog.cpp:92:bughuntingDivByZero -hog.cpp:93:bughuntingDivByZero -hog.cpp:94:bughuntingDivByZero -hog.cpp:95:bughuntingDivByZero - diff --git a/test/bug-hunting/cve/CVE-2019-15939/hog.cpp b/test/bug-hunting/cve/CVE-2019-15939/hog.cpp deleted file mode 100644 index 5a54cca374e..00000000000 --- a/test/bug-hunting/cve/CVE-2019-15939/hog.cpp +++ /dev/null @@ -1,3619 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// - // - // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. - // - // By downloading, copying, installing or using the software you agree to this license. - // If you do not agree to this license, do not download, install, - // copy or use the software. - // - // - // License Agreement - // For Open Source Computer Vision Library - // - // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. - // Copyright (C) 2009, Willow Garage Inc., all rights reserved. - // Third party copyrights are property of their respective owners. - // - // Redistribution and use in source and binary forms, with or without modification, - // are permitted provided that the following conditions are met: - // - // * Redistribution's of source code must retain the above copyright notice, - // this list of conditions and the following disclaimer. - // - // * Redistribution's 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. - // - // * The name of the copyright holders may not be used to endorse or promote products - // derived from this software without specific prior written permission. - // - // This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. - // - //M*/ - -#include "precomp.hpp" -#include "cascadedetect.hpp" -#include "opencv2/core/core_c.h" -#include "opencv2/core/hal/intrin.hpp" -#include "opencl_kernels_objdetect.hpp" - -#include -#include -#include - -/****************************************************************************************\ - The code below is implementation of HOG (Histogram-of-Oriented Gradients) - descriptor and object detection, introduced by Navneet Dalal and Bill Triggs. - - The computed feature vectors are compatible with the - INRIA Object Detection and Localization Toolkit - (http://pascal.inrialpes.fr/soft/olt/) -\****************************************************************************************/ - -namespace cv -{ - -#define NTHREADS 256 - - enum {DESCR_FORMAT_COL_BY_COL, DESCR_FORMAT_ROW_BY_ROW}; - - static int numPartsWithin(int size, int part_size, int stride) - { - CV_Assert(stride != 0); - return (size - part_size + stride) / stride; - } - - static Size numPartsWithin(cv::Size size, cv::Size part_size, - cv::Size stride) - { - return Size(numPartsWithin(size.width, part_size.width, stride.width), - numPartsWithin(size.height, part_size.height, stride.height)); - } - - static size_t getBlockHistogramSize(Size block_size, Size cell_size, int nbins) - { - CV_Assert(!cell_size.empty()); - Size cells_per_block = Size(block_size.width / cell_size.width, - block_size.height / cell_size.height); - return (size_t)(nbins * cells_per_block.area()); - } - - size_t HOGDescriptor::getDescriptorSize() const - { - CV_Assert(blockSize.width % cellSize.width == 0 && - blockSize.height % cellSize.height == 0); - CV_Assert((winSize.width - blockSize.width) % blockStride.width == 0 && - (winSize.height - blockSize.height) % blockStride.height == 0 ); - - return (size_t)nbins* - (blockSize.width/cellSize.width)* - (blockSize.height/cellSize.height)* - ((winSize.width - blockSize.width)/blockStride.width + 1)* - ((winSize.height - blockSize.height)/blockStride.height + 1); - } - - double HOGDescriptor::getWinSigma() const - { - return winSigma > 0 ? winSigma : (blockSize.width + blockSize.height)/8.; - } - - bool HOGDescriptor::checkDetectorSize() const - { - size_t detectorSize = svmDetector.size(), descriptorSize = getDescriptorSize(); - return detectorSize == 0 || - detectorSize == descriptorSize || - detectorSize == descriptorSize + 1; - } - - void HOGDescriptor::setSVMDetector(InputArray _svmDetector) - { - _svmDetector.getMat().convertTo(svmDetector, CV_32F); - CV_Assert(checkDetectorSize()); - - Mat detector_reordered(1, (int)svmDetector.size(), CV_32FC1); - - size_t block_hist_size = getBlockHistogramSize(blockSize, cellSize, nbins); - cv::Size blocks_per_img = numPartsWithin(winSize, blockSize, blockStride); - - for (int i = 0; i < blocks_per_img.height; ++i) - for (int j = 0; j < blocks_per_img.width; ++j) - { - const float *src = &svmDetector[0] + (j * blocks_per_img.height + i) * block_hist_size; - float *dst = detector_reordered.ptr() + (i * blocks_per_img.width + j) * block_hist_size; - for (size_t k = 0; k < block_hist_size; ++k) - dst[k] = src[k]; - } - size_t descriptor_size = getDescriptorSize(); - free_coef = svmDetector.size() > descriptor_size ? svmDetector[descriptor_size] : 0; - detector_reordered.copyTo(oclSvmDetector); - } - -#define CV_TYPE_NAME_HOG_DESCRIPTOR "opencv-object-detector-hog" - - bool HOGDescriptor::read(FileNode& obj) - { - CV_Assert(!obj["winSize"].empty()); - - if (!obj.isMap()) - return false; - FileNodeIterator it = obj["winSize"].begin(); - it >> winSize.width >> winSize.height; CV_Assert(!winSize.empty()); - it = obj["blockSize"].begin(); - it >> blockSize.width >> blockSize.height; CV_Assert(!blockSize.empty()); - it = obj["blockStride"].begin(); - it >> blockStride.width >> blockStride.height; CV_Assert(!blockStride.empty()); - it = obj["cellSize"].begin(); - it >> cellSize.width >> cellSize.height; CV_Assert(!cellSize.empty()); - obj["nbins"] >> nbins; CV_Assert(nbins > 0); - obj["derivAperture"] >> derivAperture; - obj["winSigma"] >> winSigma; - obj["histogramNormType"] >> histogramNormType; - obj["L2HysThreshold"] >> L2HysThreshold; - obj["gammaCorrection"] >> gammaCorrection; - obj["nlevels"] >> nlevels; CV_Assert(nlevels > 0); - if (obj["signedGradient"].empty()) - signedGradient = false; - else - obj["signedGradient"] >> signedGradient; - - FileNode vecNode = obj["SVMDetector"]; - if (vecNode.isSeq()) - { - std::vector _svmDetector; - vecNode >> _svmDetector; - setSVMDetector(_svmDetector); - } - return true; - } - - void HOGDescriptor::write(FileStorage& fs, const String& objName) const - { - if (!objName.empty()) - fs << objName; - - fs << "{" CV_TYPE_NAME_HOG_DESCRIPTOR - << "winSize" << winSize - << "blockSize" << blockSize - << "blockStride" << blockStride - << "cellSize" << cellSize - << "nbins" << nbins - << "derivAperture" << derivAperture - << "winSigma" << getWinSigma() - << "histogramNormType" << histogramNormType - << "L2HysThreshold" << L2HysThreshold - << "gammaCorrection" << gammaCorrection - << "nlevels" << nlevels - << "signedGradient" << signedGradient; - if (!svmDetector.empty()) - fs << "SVMDetector" << svmDetector; - fs << "}"; - } - - bool HOGDescriptor::load(const String& filename, const String& objname) - { - FileStorage fs(filename, FileStorage::READ); - FileNode obj = !objname.empty() ? fs[objname] : fs.getFirstTopLevelNode(); - return read(obj); - } - - void HOGDescriptor::save(const String& filename, const String& objName) const - { - FileStorage fs(filename, FileStorage::WRITE); - write(fs, !objName.empty() ? objName : FileStorage::getDefaultObjectName(filename)); - } - - void HOGDescriptor::copyTo(HOGDescriptor& c) const - { - c.winSize = winSize; - c.blockSize = blockSize; - c.blockStride = blockStride; - c.cellSize = cellSize; - c.nbins = nbins; - c.derivAperture = derivAperture; - c.winSigma = winSigma; - c.histogramNormType = histogramNormType; - c.L2HysThreshold = L2HysThreshold; - c.gammaCorrection = gammaCorrection; - c.setSVMDetector(svmDetector); - c.nlevels = nlevels; - c.signedGradient = signedGradient; - } - - void HOGDescriptor::computeGradient(const Mat& img, Mat& grad, Mat& qangle, - Size paddingTL, Size paddingBR) const - { - CV_INSTRUMENT_REGION(); - - CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 ); - - Size gradsize(img.cols + paddingTL.width + paddingBR.width, - img.rows + paddingTL.height + paddingBR.height); - grad.create(gradsize, CV_32FC2); // - qangle.create(gradsize, CV_8UC2); // [0..nbins-1] - quantized gradient orientation - - Size wholeSize; - Point roiofs; - img.locateROI(wholeSize, roiofs); - - int i, x, y; - int cn = img.channels(); - - Mat_ _lut(1, 256); - const float* const lut = &_lut(0,0); -#if CV_SIMD128 - v_float32x4 idx(0.0f, 1.0f, 2.0f, 3.0f); - v_float32x4 ifour = v_setall_f32(4.0); - - float* const _data = &_lut(0, 0); - if (gammaCorrection) - for (i = 0; i < 256; i += 4) - { - v_store(_data + i, v_sqrt(idx)); - idx += ifour; - } - else - for (i = 0; i < 256; i += 4) - { - v_store(_data + i, idx); - idx += ifour; - } -#else - if (gammaCorrection) - for (i = 0; i < 256; i++) - _lut(0,i) = std::sqrt((float)i); - else - for (i = 0; i < 256; i++) - _lut(0,i) = (float)i; -#endif - - AutoBuffer mapbuf(gradsize.width + gradsize.height + 4); - int* xmap = mapbuf.data() + 1; - int* ymap = xmap + gradsize.width + 2; - - const int borderType = (int)BORDER_REFLECT_101; - - for (x = -1; x < gradsize.width + 1; x++) - xmap[x] = borderInterpolate(x - paddingTL.width + roiofs.x, - wholeSize.width, borderType) - roiofs.x; - for (y = -1; y < gradsize.height + 1; y++) - ymap[y] = borderInterpolate(y - paddingTL.height + roiofs.y, - wholeSize.height, borderType) - roiofs.y; - - // x- & y- derivatives for the whole row - int width = gradsize.width; - AutoBuffer _dbuf(width*4); - float* const dbuf = _dbuf.data(); - Mat Dx(1, width, CV_32F, dbuf); - Mat Dy(1, width, CV_32F, dbuf + width); - Mat Mag(1, width, CV_32F, dbuf + width*2); - Mat Angle(1, width, CV_32F, dbuf + width*3); - - if (cn == 3) - { - int end = gradsize.width + 2; - xmap -= 1, x = 0; -#if CV_SIMD128 - for ( ; x <= end - 4; x += 4) - { - v_int32x4 mul_res = v_load(xmap + x); - mul_res += mul_res + mul_res; - v_store(xmap + x, mul_res); - } -#endif - for ( ; x < end; ++x) - xmap[x] *= 3; - xmap += 1; - } - - float angleScale = signedGradient ? (float)(nbins/(2.0*CV_PI)) : (float)(nbins/CV_PI); - for (y = 0; y < gradsize.height; y++) - { - const uchar* imgPtr = img.ptr(ymap[y]); - //In case subimage is used ptr() generates an assert for next and prev rows - //(see http://code.opencv.org/issues/4149) - const uchar* prevPtr = img.data + img.step*ymap[y-1]; - const uchar* nextPtr = img.data + img.step*ymap[y+1]; - - float* gradPtr = grad.ptr(y); - uchar* qanglePtr = qangle.ptr(y); - - if (cn == 1) - { - for (x = 0; x < width; x++) - { - int x1 = xmap[x]; - dbuf[x] = (float)(lut[imgPtr[xmap[x+1]]] - lut[imgPtr[xmap[x-1]]]); - dbuf[width + x] = (float)(lut[nextPtr[x1]] - lut[prevPtr[x1]]); - } - } - else - { - x = 0; -#if CV_SIMD128 - for ( ; x <= width - 4; x += 4) - { - int x0 = xmap[x], x1 = xmap[x+1], x2 = xmap[x+2], x3 = xmap[x+3]; - typedef const uchar* const T; - T p02 = imgPtr + xmap[x+1], p00 = imgPtr + xmap[x-1]; - T p12 = imgPtr + xmap[x+2], p10 = imgPtr + xmap[x]; - T p22 = imgPtr + xmap[x+3], p20 = p02; - T p32 = imgPtr + xmap[x+4], p30 = p12; - - v_float32x4 _dx0 = v_float32x4(lut[p02[0]], lut[p12[0]], lut[p22[0]], lut[p32[0]]) - - v_float32x4(lut[p00[0]], lut[p10[0]], lut[p20[0]], lut[p30[0]]); - v_float32x4 _dx1 = v_float32x4(lut[p02[1]], lut[p12[1]], lut[p22[1]], lut[p32[1]]) - - v_float32x4(lut[p00[1]], lut[p10[1]], lut[p20[1]], lut[p30[1]]); - v_float32x4 _dx2 = v_float32x4(lut[p02[2]], lut[p12[2]], lut[p22[2]], lut[p32[2]]) - - v_float32x4(lut[p00[2]], lut[p10[2]], lut[p20[2]], lut[p30[2]]); - - v_float32x4 _dy0 = v_float32x4(lut[nextPtr[x0]], lut[nextPtr[x1]], lut[nextPtr[x2]], lut[nextPtr[x3]]) - - v_float32x4(lut[prevPtr[x0]], lut[prevPtr[x1]], lut[prevPtr[x2]], lut[prevPtr[x3]]); - v_float32x4 _dy1 = v_float32x4(lut[nextPtr[x0+1]], lut[nextPtr[x1+1]], lut[nextPtr[x2+1]], lut[nextPtr[x3+1]]) - - v_float32x4(lut[prevPtr[x0+1]], lut[prevPtr[x1+1]], lut[prevPtr[x2+1]], lut[prevPtr[x3+1]]); - v_float32x4 _dy2 = v_float32x4(lut[nextPtr[x0+2]], lut[nextPtr[x1+2]], lut[nextPtr[x2+2]], lut[nextPtr[x3+2]]) - - v_float32x4(lut[prevPtr[x0+2]], lut[prevPtr[x1+2]], lut[prevPtr[x2+2]], lut[prevPtr[x3+2]]); - - v_float32x4 _mag0 = (_dx0 * _dx0) + (_dy0 * _dy0); - v_float32x4 _mag1 = (_dx1 * _dx1) + (_dy1 * _dy1); - v_float32x4 _mag2 = (_dx2 * _dx2) + (_dy2 * _dy2); - - v_float32x4 mask = v_reinterpret_as_f32(_mag2 > _mag1); - _dx2 = v_select(mask, _dx2, _dx1); - _dy2 = v_select(mask, _dy2, _dy1); - - mask = v_reinterpret_as_f32(v_max(_mag2, _mag1) > _mag0); - _dx2 = v_select(mask, _dx2, _dx0); - _dy2 = v_select(mask, _dy2, _dy0); - - v_store(dbuf + x, _dx2); - v_store(dbuf + x + width, _dy2); - } -#endif - for ( ; x < width; x++) - { - int x1 = xmap[x]; - float dx0, dy0, dx, dy, mag0, mag; - const uchar* p2 = imgPtr + xmap[x+1]; - const uchar* p0 = imgPtr + xmap[x-1]; - - dx0 = lut[p2[2]] - lut[p0[2]]; - dy0 = lut[nextPtr[x1+2]] - lut[prevPtr[x1+2]]; - mag0 = dx0*dx0 + dy0*dy0; - - dx = lut[p2[1]] - lut[p0[1]]; - dy = lut[nextPtr[x1+1]] - lut[prevPtr[x1+1]]; - mag = dx*dx + dy*dy; - if (mag0 < mag) - { - dx0 = dx; - dy0 = dy; - mag0 = mag; - } - - dx = lut[p2[0]] - lut[p0[0]]; - dy = lut[nextPtr[x1]] - lut[prevPtr[x1]]; - mag = dx*dx + dy*dy; - if (mag0 < mag) - { - dx0 = dx; - dy0 = dy; - mag0 = mag; - } - - dbuf[x] = dx0; - dbuf[x+width] = dy0; - } - } - - // computing angles and magnidutes - cartToPolar( Dx, Dy, Mag, Angle, false ); - - // filling the result matrix - x = 0; -#if CV_SIMD128 - v_float32x4 fhalf = v_setall_f32(0.5f); - v_float32x4 _angleScale = v_setall_f32(angleScale), fone = v_setall_f32(1.0f); - v_int32x4 ione = v_setall_s32(1), _nbins = v_setall_s32(nbins), izero = v_setzero_s32(); - - for ( ; x <= width - 4; x += 4) - { - int x2 = x << 1; - v_float32x4 _mag = v_load(dbuf + x + (width << 1)); - v_float32x4 _angle = v_load(dbuf + x + width * 3); - _angle = (_angleScale * _angle) - fhalf; - - v_int32x4 _hidx = v_floor(_angle); - _angle -= v_cvt_f32(_hidx); - - v_float32x4 ft0 = _mag * (fone - _angle); - v_float32x4 ft1 = _mag * _angle; - - v_store_interleave(gradPtr + x2, ft0, ft1); - - v_int32x4 mask0 = _hidx >> 31; - v_int32x4 it0 = mask0 & _nbins; - mask0 = (_hidx >= _nbins); - v_int32x4 it1 = mask0 & _nbins; - _hidx += (it0 - it1); - - it0 = v_reinterpret_as_s32(v_pack(v_pack(_hidx, izero), v_reinterpret_as_s16(izero))); - _hidx += ione; - _hidx &= (_hidx < _nbins); - it1 = v_reinterpret_as_s32(v_pack(v_pack(_hidx, izero), v_reinterpret_as_s16(izero))); - v_uint8x16 it2, it3; - v_zip(v_reinterpret_as_u8(it0), v_reinterpret_as_u8(it1), it2, it3); - - v_store_low(qanglePtr + x2, it2); - } -#endif - for ( ; x < width; x++) - { - float mag = dbuf[x+width*2], angle = dbuf[x+width*3]*angleScale - 0.5f; - int hidx = cvFloor(angle); - angle -= hidx; - gradPtr[x*2] = mag*(1.f - angle); - gradPtr[x*2+1] = mag*angle; - - if (hidx < 0) - hidx += nbins; - else if (hidx >= nbins) - hidx -= nbins; - - CV_Assert((unsigned)hidx < (unsigned)nbins ); - - qanglePtr[x*2] = (uchar)hidx; - hidx++; - hidx &= hidx < nbins ? -1 : 0; - qanglePtr[x*2+1] = (uchar)hidx; - } - } - } - - struct HOGCache - { - struct BlockData - { - BlockData() : - histOfs(0), imgOffset() - {} - - int histOfs; - Point imgOffset; - }; - - struct PixData - { - size_t gradOfs, qangleOfs; - int histOfs[4]; - float histWeights[4]; - float gradWeight; - }; - - HOGCache(); - HOGCache(const HOGDescriptor* descriptor, - const Mat& img, const Size& paddingTL, const Size& paddingBR, - bool useCache, const Size& cacheStride); - virtual ~HOGCache() {} - virtual void init(const HOGDescriptor* descriptor, - const Mat& img, const Size& paddingTL, const Size& paddingBR, - bool useCache, const Size& cacheStride); - - Size windowsInImage(const Size& imageSize, const Size& winStride) const; - Rect getWindow(const Size& imageSize, const Size& winStride, int idx) const; - - const float* getBlock(Point pt, float* buf); - virtual void normalizeBlockHistogram(float* histogram) const; - - std::vector pixData; - std::vector blockData; - - bool useCache; - std::vector ymaxCached; - Size winSize; - Size cacheStride; - Size nblocks, ncells; - int blockHistogramSize; - int count1, count2, count4; - Point imgoffset; - Mat_ blockCache; - Mat_ blockCacheFlags; - - Mat grad, qangle; - const HOGDescriptor* descriptor; - }; - - HOGCache::HOGCache() : - blockHistogramSize(), count1(), count2(), count4() - { - useCache = false; - descriptor = 0; - } - - HOGCache::HOGCache(const HOGDescriptor* _descriptor, - const Mat& _img, const Size& _paddingTL, const Size& _paddingBR, - bool _useCache, const Size& _cacheStride) - { - init(_descriptor, _img, _paddingTL, _paddingBR, _useCache, _cacheStride); - } - - void HOGCache::init(const HOGDescriptor* _descriptor, - const Mat& _img, const Size& _paddingTL, const Size& _paddingBR, - bool _useCache, const Size& _cacheStride) - { - descriptor = _descriptor; - cacheStride = _cacheStride; - useCache = _useCache; - - descriptor->computeGradient(_img, grad, qangle, _paddingTL, _paddingBR); - imgoffset = _paddingTL; - - winSize = descriptor->winSize; - Size blockSize = descriptor->blockSize; - Size blockStride = descriptor->blockStride; - Size cellSize = descriptor->cellSize; - int i, j, nbins = descriptor->nbins; - int rawBlockSize = blockSize.width*blockSize.height; - - nblocks = Size((winSize.width - blockSize.width)/blockStride.width + 1, - (winSize.height - blockSize.height)/blockStride.height + 1); - ncells = Size(blockSize.width/cellSize.width, blockSize.height/cellSize.height); - blockHistogramSize = ncells.width*ncells.height*nbins; - - if (useCache) - { - Size cacheSize((grad.cols - blockSize.width)/cacheStride.width+1, - (winSize.height/cacheStride.height)+1); - - blockCache.create(cacheSize.height, cacheSize.width*blockHistogramSize); - blockCacheFlags.create(cacheSize); - - size_t cacheRows = blockCache.rows; - ymaxCached.resize(cacheRows); - for (size_t ii = 0; ii < cacheRows; ii++) - ymaxCached[ii] = -1; - } - - Mat_ weights(blockSize); - float sigma = (float)descriptor->getWinSigma(); - float scale = 1.f/(sigma*sigma*2); - - { - AutoBuffer di(blockSize.height), dj(blockSize.width); - float* _di = di.data(), *_dj = dj.data(); - float bh = blockSize.height * 0.5f, bw = blockSize.width * 0.5f; - - i = 0; - #if CV_SIMD128 - v_float32x4 idx(0.0f, 1.0f, 2.0f, 3.0f); - v_float32x4 _bw = v_setall_f32(bw), _bh = v_setall_f32(bh); - v_float32x4 ifour = v_setall_f32(4.0); - - for (; i <= blockSize.height - 4; i += 4) - { - v_float32x4 t = idx - _bh; - t *= t; - idx += ifour; - v_store(_di + i, t); - } - #endif - for ( ; i < blockSize.height; ++i) - { - _di[i] = i - bh; - _di[i] *= _di[i]; - } - - j = 0; - #if CV_SIMD128 - idx = v_float32x4(0.0f, 1.0f, 2.0f, 3.0f); - - for (; j <= blockSize.height - 4; j += 4) - { - v_float32x4 t = idx - _bw; - t *= t; - idx += ifour; - v_store(_dj + j, t); - } - #endif - for ( ; j < blockSize.width; ++j) - { - _dj[j] = j - bw; - _dj[j] *= _dj[j]; - } - - for (i = 0; i < blockSize.height; i++) - for (j = 0; j < blockSize.width; j++) - weights(i,j) = std::exp(-(_di[i] + _dj[j])*scale); - } - - blockData.resize(nblocks.width*nblocks.height); - pixData.resize(rawBlockSize*3); - - // Initialize 2 lookup tables, pixData & blockData. - // Here is why: - // - // The detection algorithm runs in 4 nested loops (at each pyramid layer): - // loop over the windows within the input image - // loop over the blocks within each window - // loop over the cells within each block - // loop over the pixels in each cell - // - // As each of the loops runs over a 2-dimensional array, - // we could get 8(!) nested loops in total, which is very-very slow. - // - // To speed the things up, we do the following: - // 1. loop over windows is unrolled in the HOGDescriptor::{compute|detect} methods; - // inside we compute the current search window using getWindow() method. - // Yes, it involves some overhead (function call + couple of divisions), - // but it's tiny in fact. - // 2. loop over the blocks is also unrolled. Inside we use pre-computed blockData[j] - // to set up gradient and histogram pointers. - // 3. loops over cells and pixels in each cell are merged - // (since there is no overlap between cells, each pixel in the block is processed once) - // and also unrolled. Inside we use PixData[k] to access the gradient values and - // update the histogram - // - - count1 = count2 = count4 = 0; - for (j = 0; j < blockSize.width; j++) - for (i = 0; i < blockSize.height; i++) - { - PixData* data = 0; - float cellX = (j+0.5f)/cellSize.width - 0.5f; - float cellY = (i+0.5f)/cellSize.height - 0.5f; - int icellX0 = cvFloor(cellX); - int icellY0 = cvFloor(cellY); - int icellX1 = icellX0 + 1, icellY1 = icellY0 + 1; - cellX -= icellX0; - cellY -= icellY0; - - if ((unsigned)icellX0 < (unsigned)ncells.width && - (unsigned)icellX1 < (unsigned)ncells.width) - { - if ((unsigned)icellY0 < (unsigned)ncells.height && - (unsigned)icellY1 < (unsigned)ncells.height) - { - data = &pixData[rawBlockSize*2 + (count4++)]; - data->histOfs[0] = (icellX0*ncells.height + icellY0)*nbins; - data->histWeights[0] = (1.f - cellX)*(1.f - cellY); - data->histOfs[1] = (icellX1*ncells.height + icellY0)*nbins; - data->histWeights[1] = cellX*(1.f - cellY); - data->histOfs[2] = (icellX0*ncells.height + icellY1)*nbins; - data->histWeights[2] = (1.f - cellX)*cellY; - data->histOfs[3] = (icellX1*ncells.height + icellY1)*nbins; - data->histWeights[3] = cellX*cellY; - } - else - { - data = &pixData[rawBlockSize + (count2++)]; - if ((unsigned)icellY0 < (unsigned)ncells.height) - { - icellY1 = icellY0; - cellY = 1.f - cellY; - } - data->histOfs[0] = (icellX0*ncells.height + icellY1)*nbins; - data->histWeights[0] = (1.f - cellX)*cellY; - data->histOfs[1] = (icellX1*ncells.height + icellY1)*nbins; - data->histWeights[1] = cellX*cellY; - data->histOfs[2] = data->histOfs[3] = 0; - data->histWeights[2] = data->histWeights[3] = 0; - } - } - else - { - if ((unsigned)icellX0 < (unsigned)ncells.width) - { - icellX1 = icellX0; - cellX = 1.f - cellX; - } - - if ((unsigned)icellY0 < (unsigned)ncells.height && - (unsigned)icellY1 < (unsigned)ncells.height) - { - data = &pixData[rawBlockSize + (count2++)]; - data->histOfs[0] = (icellX1*ncells.height + icellY0)*nbins; - data->histWeights[0] = cellX*(1.f - cellY); - data->histOfs[1] = (icellX1*ncells.height + icellY1)*nbins; - data->histWeights[1] = cellX*cellY; - data->histOfs[2] = data->histOfs[3] = 0; - data->histWeights[2] = data->histWeights[3] = 0; - } - else - { - data = &pixData[count1++]; - if ((unsigned)icellY0 < (unsigned)ncells.height) - { - icellY1 = icellY0; - cellY = 1.f - cellY; - } - data->histOfs[0] = (icellX1*ncells.height + icellY1)*nbins; - data->histWeights[0] = cellX*cellY; - data->histOfs[1] = data->histOfs[2] = data->histOfs[3] = 0; - data->histWeights[1] = data->histWeights[2] = data->histWeights[3] = 0; - } - } - data->gradOfs = (grad.cols*i + j)*2; - data->qangleOfs = (qangle.cols*i + j)*2; - data->gradWeight = weights(i,j); - } - - assert( count1 + count2 + count4 == rawBlockSize ); - // defragment pixData - for (j = 0; j < count2; j++) - pixData[j + count1] = pixData[j + rawBlockSize]; - for (j = 0; j < count4; j++) - pixData[j + count1 + count2] = pixData[j + rawBlockSize*2]; - count2 += count1; - count4 += count2; - - // initialize blockData - for (j = 0; j < nblocks.width; j++) - for (i = 0; i < nblocks.height; i++) - { - BlockData& data = blockData[j*nblocks.height + i]; - data.histOfs = (j*nblocks.height + i)*blockHistogramSize; - data.imgOffset = Point(j*blockStride.width,i*blockStride.height); - } - } - - const float* HOGCache::getBlock(Point pt, float* buf) - { - float* blockHist = buf; - assert(descriptor != 0); - -// Size blockSize = descriptor->blockSize; - pt += imgoffset; - -// CV_Assert( (unsigned)pt.x <= (unsigned)(grad.cols - blockSize.width) && -// (unsigned)pt.y <= (unsigned)(grad.rows - blockSize.height) ); - - if (useCache) - { - CV_Assert( pt.x % cacheStride.width == 0 && - pt.y % cacheStride.height == 0 ); - Point cacheIdx(pt.x/cacheStride.width, - (pt.y/cacheStride.height) % blockCache.rows); - if (pt.y != ymaxCached[cacheIdx.y]) - { - Mat_ cacheRow = blockCacheFlags.row(cacheIdx.y); - cacheRow = (uchar)0; - ymaxCached[cacheIdx.y] = pt.y; - } - - blockHist = &blockCache[cacheIdx.y][cacheIdx.x*blockHistogramSize]; - uchar& computedFlag = blockCacheFlags(cacheIdx.y, cacheIdx.x); - if (computedFlag != 0) - return blockHist; - computedFlag = (uchar)1; // set it at once, before actual computing - } - - int k, C1 = count1, C2 = count2, C4 = count4; - const float* gradPtr = grad.ptr(pt.y) + pt.x*2; - const uchar* qanglePtr = qangle.ptr(pt.y) + pt.x*2; - -// CV_Assert( blockHist != 0 ); - memset(blockHist, 0, sizeof(float) * blockHistogramSize); - - const PixData* _pixData = &pixData[0]; - - for (k = 0; k < C1; k++) - { - const PixData& pk = _pixData[k]; - const float* const a = gradPtr + pk.gradOfs; - float w = pk.gradWeight*pk.histWeights[0]; - const uchar* h = qanglePtr + pk.qangleOfs; - int h0 = h[0], h1 = h[1]; - - float* hist = blockHist + pk.histOfs[0]; - float t0 = hist[h0] + a[0]*w; - float t1 = hist[h1] + a[1]*w; - hist[h0] = t0; hist[h1] = t1; - } - -#if CV_SIMD128 - float hist0[4], hist1[4]; - for ( ; k < C2; k++) - { - const PixData& pk = _pixData[k]; - const float* const a = gradPtr + pk.gradOfs; - const uchar* const h = qanglePtr + pk.qangleOfs; - int h0 = h[0], h1 = h[1]; - - v_float32x4 _a0 = v_setall_f32(a[0]), _a1 = v_setall_f32(a[1]); - v_float32x4 w = v_setall_f32(pk.gradWeight) * v_load(pk.histWeights); - v_float32x4 _t0 = _a0 * w, _t1 = _a1 * w; - - v_store(hist0, _t0); - v_store(hist1, _t1); - - float* hist = blockHist + pk.histOfs[0]; - float t0 = hist[h0] + hist0[0]; - float t1 = hist[h1] + hist1[0]; - hist[h0] = t0; hist[h1] = t1; - - hist = blockHist + pk.histOfs[1]; - t0 = hist[h0] + hist0[1]; - t1 = hist[h1] + hist1[1]; - hist[h0] = t0; hist[h1] = t1; - } -#else - for ( ; k < C2; k++) - { - const PixData& pk = _pixData[k]; - const float* const a = gradPtr + pk.gradOfs; - float w, t0, t1, a0 = a[0], a1 = a[1]; - const uchar* const h = qanglePtr + pk.qangleOfs; - int h0 = h[0], h1 = h[1]; - - float* hist = blockHist + pk.histOfs[0]; - w = pk.gradWeight*pk.histWeights[0]; - t0 = hist[h0] + a0*w; - t1 = hist[h1] + a1*w; - hist[h0] = t0; hist[h1] = t1; - - hist = blockHist + pk.histOfs[1]; - w = pk.gradWeight*pk.histWeights[1]; - t0 = hist[h0] + a0*w; - t1 = hist[h1] + a1*w; - hist[h0] = t0; hist[h1] = t1; - } -#endif - -#if CV_SIMD128 - for ( ; k < C4; k++) - { - const PixData& pk = _pixData[k]; - const float* const a = gradPtr + pk.gradOfs; - const uchar* const h = qanglePtr + pk.qangleOfs; - int h0 = h[0], h1 = h[1]; - - v_float32x4 _a0 = v_setall_f32(a[0]), _a1 = v_setall_f32(a[1]); - v_float32x4 w = v_setall_f32(pk.gradWeight) * v_load(pk.histWeights); - v_float32x4 _t0 = _a0 * w, _t1 = _a1 * w; - - v_store(hist0, _t0); - v_store(hist1, _t1); - - float* hist = blockHist + pk.histOfs[0]; - float t0 = hist[h0] + hist0[0]; - float t1 = hist[h1] + hist1[0]; - hist[h0] = t0; hist[h1] = t1; - - hist = blockHist + pk.histOfs[1]; - t0 = hist[h0] + hist0[1]; - t1 = hist[h1] + hist1[1]; - hist[h0] = t0; hist[h1] = t1; - - hist = blockHist + pk.histOfs[2]; - t0 = hist[h0] + hist0[2]; - t1 = hist[h1] + hist1[2]; - hist[h0] = t0; hist[h1] = t1; - - hist = blockHist + pk.histOfs[3]; - t0 = hist[h0] + hist0[3]; - t1 = hist[h1] + hist1[3]; - hist[h0] = t0; hist[h1] = t1; - } -#else - for ( ; k < C4; k++) - { - const PixData& pk = _pixData[k]; - const float* a = gradPtr + pk.gradOfs; - float w, t0, t1, a0 = a[0], a1 = a[1]; - const uchar* h = qanglePtr + pk.qangleOfs; - int h0 = h[0], h1 = h[1]; - - float* hist = blockHist + pk.histOfs[0]; - w = pk.gradWeight*pk.histWeights[0]; - t0 = hist[h0] + a0*w; - t1 = hist[h1] + a1*w; - hist[h0] = t0; hist[h1] = t1; - - hist = blockHist + pk.histOfs[1]; - w = pk.gradWeight*pk.histWeights[1]; - t0 = hist[h0] + a0*w; - t1 = hist[h1] + a1*w; - hist[h0] = t0; hist[h1] = t1; - - hist = blockHist + pk.histOfs[2]; - w = pk.gradWeight*pk.histWeights[2]; - t0 = hist[h0] + a0*w; - t1 = hist[h1] + a1*w; - hist[h0] = t0; hist[h1] = t1; - - hist = blockHist + pk.histOfs[3]; - w = pk.gradWeight*pk.histWeights[3]; - t0 = hist[h0] + a0*w; - t1 = hist[h1] + a1*w; - hist[h0] = t0; hist[h1] = t1; - } -#endif - - normalizeBlockHistogram(blockHist); - - return blockHist; - } - - void HOGCache::normalizeBlockHistogram(float* _hist) const - { - float* hist = &_hist[0], sum = 0.0f, partSum[4]; - size_t i = 0, sz = blockHistogramSize; - -#if CV_SIMD128 - v_float32x4 p0 = v_load(hist); - v_float32x4 s = p0 * p0; - - for (i = 4; i <= sz - 4; i += 4) - { - p0 = v_load(hist + i); - s += p0 * p0; - } - v_store(partSum, s); -#else - partSum[0] = 0.0f; - partSum[1] = 0.0f; - partSum[2] = 0.0f; - partSum[3] = 0.0f; - for ( ; i <= sz - 4; i += 4) - { - partSum[0] += hist[i] * hist[i]; - partSum[1] += hist[i+1] * hist[i+1]; - partSum[2] += hist[i+2] * hist[i+2]; - partSum[3] += hist[i+3] * hist[i+3]; - } -#endif - float t0 = partSum[0] + partSum[1]; - float t1 = partSum[2] + partSum[3]; - sum = t0 + t1; - for ( ; i < sz; ++i) - sum += hist[i]*hist[i]; - - float scale = 1.f/(std::sqrt(sum)+sz*0.1f), thresh = (float)descriptor->L2HysThreshold; - i = 0, sum = 0.0f; - -#if CV_SIMD128 - v_float32x4 _scale = v_setall_f32(scale); - static v_float32x4 _threshold = v_setall_f32(thresh); - - v_float32x4 p = _scale * v_load(hist); - p = v_min(p, _threshold); - s = p * p; - v_store(hist, p); - - for (i = 4; i <= sz - 4; i += 4) - { - p = v_load(hist + i); - p *= _scale; - p = v_min(p, _threshold); - s += p * p; - v_store(hist + i, p); - } - - v_store(partSum, s); -#else - partSum[0] = 0.0f; - partSum[1] = 0.0f; - partSum[2] = 0.0f; - partSum[3] = 0.0f; - for ( ; i <= sz - 4; i += 4) - { - hist[i] = std::min(hist[i]*scale, thresh); - hist[i+1] = std::min(hist[i+1]*scale, thresh); - hist[i+2] = std::min(hist[i+2]*scale, thresh); - hist[i+3] = std::min(hist[i+3]*scale, thresh); - partSum[0] += hist[i]*hist[i]; - partSum[1] += hist[i+1]*hist[i+1]; - partSum[2] += hist[i+2]*hist[i+2]; - partSum[3] += hist[i+3]*hist[i+3]; - } -#endif - t0 = partSum[0] + partSum[1]; - t1 = partSum[2] + partSum[3]; - sum = t0 + t1; - for ( ; i < sz; ++i) - { - hist[i] = std::min(hist[i]*scale, thresh); - sum += hist[i]*hist[i]; - } - - scale = 1.f/(std::sqrt(sum)+1e-3f), i = 0; -#if CV_SIMD128 - v_float32x4 _scale2 = v_setall_f32(scale); - for ( ; i <= sz - 4; i += 4) - { - v_float32x4 t = _scale2 * v_load(hist + i); - v_store(hist + i, t); - } -#endif - for ( ; i < sz; ++i) - hist[i] *= scale; - } - - Size HOGCache::windowsInImage(const Size& imageSize, const Size& winStride) const - { - return Size((imageSize.width - winSize.width)/winStride.width + 1, - (imageSize.height - winSize.height)/winStride.height + 1); - } - - Rect HOGCache::getWindow(const Size& imageSize, const Size& winStride, int idx) const - { - int nwindowsX = (imageSize.width - winSize.width)/winStride.width + 1; - int y = idx / nwindowsX; - int x = idx - nwindowsX*y; - return Rect( x*winStride.width, y*winStride.height, winSize.width, winSize.height ); - } - - static inline int gcd(int a, int b) - { - if (a < b) - std::swap(a, b); - while (b > 0) - { - int r = a % b; - a = b; - b = r; - } - return a; - } - -#ifdef HAVE_OPENCL - - static bool ocl_compute_gradients_8UC1(int height, int width, InputArray _img, float angle_scale, - UMat grad, UMat qangle, bool correct_gamma, int nbins) - { - ocl::Kernel k("compute_gradients_8UC1_kernel", ocl::objdetect::objdetect_hog_oclsrc); - if (k.empty()) - return false; - - UMat img = _img.getUMat(); - - size_t localThreads[3] = { NTHREADS, 1, 1 }; - size_t globalThreads[3] = { (size_t)width, (size_t)height, 1 }; - char correctGamma = (correct_gamma) ? 1 : 0; - int grad_quadstep = (int)grad.step >> 3; - int qangle_elem_size = CV_ELEM_SIZE1(qangle.type()); - int qangle_step = (int)qangle.step / (2 * qangle_elem_size); - - int idx = 0; - idx = k.set(idx, height); - idx = k.set(idx, width); - idx = k.set(idx, (int)img.step1()); - idx = k.set(idx, grad_quadstep); - idx = k.set(idx, qangle_step); - idx = k.set(idx, ocl::KernelArg::PtrReadOnly(img)); - idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(grad)); - idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(qangle)); - idx = k.set(idx, angle_scale); - idx = k.set(idx, correctGamma); - idx = k.set(idx, nbins); - - return k.run(2, globalThreads, localThreads, false); - } - - static bool ocl_computeGradient(InputArray img, UMat grad, UMat qangle, int nbins, Size effect_size, bool gamma_correction, bool signedGradient) - { - float angleScale = signedGradient ? (float)(nbins/(2.0*CV_PI)) : (float)(nbins/CV_PI); - - return ocl_compute_gradients_8UC1(effect_size.height, effect_size.width, img, - angleScale, grad, qangle, gamma_correction, nbins); - } - -#define CELL_WIDTH 8 -#define CELL_HEIGHT 8 -#define CELLS_PER_BLOCK_X 2 -#define CELLS_PER_BLOCK_Y 2 - - static bool ocl_compute_hists(int nbins, int block_stride_x, int block_stride_y, int height, int width, - UMat grad, UMat qangle, UMat gauss_w_lut, UMat block_hists, size_t block_hist_size) - { - ocl::Kernel k("compute_hists_lut_kernel", ocl::objdetect::objdetect_hog_oclsrc); - if (k.empty()) - return false; - bool is_cpu = cv::ocl::Device::getDefault().type() == cv::ocl::Device::TYPE_CPU; - cv::String opts; - if (is_cpu) - opts = "-D CPU "; - else - opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); - k.create("compute_hists_lut_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); - if (k.empty()) - return false; - - int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x)/block_stride_x; - int img_block_height = (height - CELLS_PER_BLOCK_Y * CELL_HEIGHT + block_stride_y)/block_stride_y; - int blocks_total = img_block_width * img_block_height; - - int qangle_elem_size = CV_ELEM_SIZE1(qangle.type()); - int grad_quadstep = (int)grad.step >> 2; - int qangle_step = (int)qangle.step / qangle_elem_size; - - int blocks_in_group = 4; - size_t localThreads[3] = { (size_t)blocks_in_group * 24, 2, 1 }; - size_t globalThreads[3] = {((img_block_width * img_block_height + blocks_in_group - 1)/blocks_in_group) * localThreads[0], 2, 1 }; - - int hists_size = (nbins * CELLS_PER_BLOCK_X * CELLS_PER_BLOCK_Y * 12) * sizeof(float); - int final_hists_size = (nbins * CELLS_PER_BLOCK_X * CELLS_PER_BLOCK_Y) * sizeof(float); - - int smem = (hists_size + final_hists_size) * blocks_in_group; - - int idx = 0; - idx = k.set(idx, block_stride_x); - idx = k.set(idx, block_stride_y); - idx = k.set(idx, nbins); - idx = k.set(idx, (int)block_hist_size); - idx = k.set(idx, img_block_width); - idx = k.set(idx, blocks_in_group); - idx = k.set(idx, blocks_total); - idx = k.set(idx, grad_quadstep); - idx = k.set(idx, qangle_step); - idx = k.set(idx, ocl::KernelArg::PtrReadOnly(grad)); - idx = k.set(idx, ocl::KernelArg::PtrReadOnly(qangle)); - idx = k.set(idx, ocl::KernelArg::PtrReadOnly(gauss_w_lut)); - idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(block_hists)); - idx = k.set(idx, (void*)NULL, (size_t)smem); - - return k.run(2, globalThreads, localThreads, false); - } - - static int power_2up(unsigned int n) - { - for (unsigned int i = 1; i<=1024; i<<=1) - if (n < i) - return i; - return -1; // Input is too big - } - - static bool ocl_normalize_hists(int nbins, int block_stride_x, int block_stride_y, - int height, int width, UMat block_hists, float threshold) - { - int block_hist_size = nbins * CELLS_PER_BLOCK_X * CELLS_PER_BLOCK_Y; - int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x) - / block_stride_x; - int img_block_height = (height - CELLS_PER_BLOCK_Y * CELL_HEIGHT + block_stride_y) - / block_stride_y; - int nthreads; - size_t globalThreads[3] = { 1, 1, 1 }; - size_t localThreads[3] = { 1, 1, 1 }; - - int idx = 0; - bool is_cpu = cv::ocl::Device::getDefault().type() == cv::ocl::Device::TYPE_CPU; - cv::String opts; - ocl::Kernel k; - if (nbins == 9) - { - k.create("normalize_hists_36_kernel", ocl::objdetect::objdetect_hog_oclsrc, ""); - if (k.empty()) - return false; - if (is_cpu) - opts = "-D CPU "; - else - opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); - k.create("normalize_hists_36_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); - if (k.empty()) - return false; - - int blocks_in_group = NTHREADS / block_hist_size; - nthreads = blocks_in_group * block_hist_size; - int num_groups = (img_block_width * img_block_height + blocks_in_group - 1)/blocks_in_group; - globalThreads[0] = nthreads * num_groups; - localThreads[0] = nthreads; - } - else - { - k.create("normalize_hists_kernel", ocl::objdetect::objdetect_hog_oclsrc, "-D WAVE_SIZE=32"); - if (k.empty()) - return false; - if (is_cpu) - opts = "-D CPU "; - else - opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); - k.create("normalize_hists_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); - if (k.empty()) - return false; - - nthreads = power_2up(block_hist_size); - globalThreads[0] = img_block_width * nthreads; - globalThreads[1] = img_block_height; - localThreads[0] = nthreads; - - if ((nthreads < 32) || (nthreads > 512)) - return false; - - idx = k.set(idx, nthreads); - idx = k.set(idx, block_hist_size); - idx = k.set(idx, img_block_width); - } - idx = k.set(idx, ocl::KernelArg::PtrReadWrite(block_hists)); - idx = k.set(idx, threshold); - idx = k.set(idx, (void*)NULL, nthreads * sizeof(float)); - - return k.run(2, globalThreads, localThreads, false); - } - - static bool ocl_extract_descrs_by_rows(int win_height, int win_width, int block_stride_y, int block_stride_x, int win_stride_y, int win_stride_x, - int height, int width, UMat block_hists, UMat descriptors, - int block_hist_size, int descr_size, int descr_width) - { - ocl::Kernel k("extract_descrs_by_rows_kernel", ocl::objdetect::objdetect_hog_oclsrc); - if (k.empty()) - return false; - - int win_block_stride_x = win_stride_x / block_stride_x; - int win_block_stride_y = win_stride_y / block_stride_y; - int img_win_width = (width - win_width + win_stride_x) / win_stride_x; - int img_win_height = (height - win_height + win_stride_y) / win_stride_y; - int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x) / - block_stride_x; - - int descriptors_quadstep = (int)descriptors.step >> 2; - - size_t globalThreads[3] = { (size_t)img_win_width * NTHREADS, (size_t)img_win_height, 1 }; - size_t localThreads[3] = { NTHREADS, 1, 1 }; - - int idx = 0; - idx = k.set(idx, block_hist_size); - idx = k.set(idx, descriptors_quadstep); - idx = k.set(idx, descr_size); - idx = k.set(idx, descr_width); - idx = k.set(idx, img_block_width); - idx = k.set(idx, win_block_stride_x); - idx = k.set(idx, win_block_stride_y); - idx = k.set(idx, ocl::KernelArg::PtrReadOnly(block_hists)); - idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(descriptors)); - - return k.run(2, globalThreads, localThreads, false); - } - - static bool ocl_extract_descrs_by_cols(int win_height, int win_width, int block_stride_y, int block_stride_x, int win_stride_y, int win_stride_x, - int height, int width, UMat block_hists, UMat descriptors, - int block_hist_size, int descr_size, int nblocks_win_x, int nblocks_win_y) - { - ocl::Kernel k("extract_descrs_by_cols_kernel", ocl::objdetect::objdetect_hog_oclsrc); - if (k.empty()) - return false; - - int win_block_stride_x = win_stride_x / block_stride_x; - int win_block_stride_y = win_stride_y / block_stride_y; - int img_win_width = (width - win_width + win_stride_x) / win_stride_x; - int img_win_height = (height - win_height + win_stride_y) / win_stride_y; - int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x) / - block_stride_x; - - int descriptors_quadstep = (int)descriptors.step >> 2; - - size_t globalThreads[3] = { (size_t)img_win_width * NTHREADS, (size_t)img_win_height, 1 }; - size_t localThreads[3] = { NTHREADS, 1, 1 }; - - int idx = 0; - idx = k.set(idx, block_hist_size); - idx = k.set(idx, descriptors_quadstep); - idx = k.set(idx, descr_size); - idx = k.set(idx, nblocks_win_x); - idx = k.set(idx, nblocks_win_y); - idx = k.set(idx, img_block_width); - idx = k.set(idx, win_block_stride_x); - idx = k.set(idx, win_block_stride_y); - idx = k.set(idx, ocl::KernelArg::PtrReadOnly(block_hists)); - idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(descriptors)); - - return k.run(2, globalThreads, localThreads, false); - } - - static bool ocl_compute(InputArray _img, Size win_stride, std::vector& _descriptors, int descr_format, Size blockSize, - Size cellSize, int nbins, Size blockStride, Size winSize, float sigma, bool gammaCorrection, double L2HysThreshold, bool signedGradient) - { - Size imgSize = _img.size(); - Size effect_size = imgSize; - - UMat grad(imgSize, CV_32FC2); - int qangle_type = ocl::Device::getDefault().isIntel() ? CV_32SC2 : CV_8UC2; - UMat qangle(imgSize, qangle_type); - - const size_t block_hist_size = getBlockHistogramSize(blockSize, cellSize, nbins); - const Size blocks_per_img = numPartsWithin(imgSize, blockSize, blockStride); - UMat block_hists(1, static_cast(block_hist_size * blocks_per_img.area()) + 256, CV_32F); - - Size wins_per_img = numPartsWithin(imgSize, winSize, win_stride); - UMat labels(1, wins_per_img.area(), CV_8U); - - float scale = 1.f / (2.f * sigma * sigma); - Mat gaussian_lut(1, 512, CV_32FC1); - int idx = 0; - for (int i=-8; i<8; i++) - for (int j=-8; j<8; j++) - gaussian_lut.at(idx++) = std::exp(-(j * j + i * i) * scale); - for (int i=-8; i<8; i++) - for (int j=-8; j<8; j++) - gaussian_lut.at(idx++) = (8.f - fabs(j + 0.5f)) * (8.f - fabs(i + 0.5f)) / 64.f; - - if (!ocl_computeGradient(_img, grad, qangle, nbins, effect_size, gammaCorrection, signedGradient)) - return false; - - UMat gauss_w_lut; - gaussian_lut.copyTo(gauss_w_lut); - if (!ocl_compute_hists(nbins, blockStride.width, blockStride.height, effect_size.height, - effect_size.width, grad, qangle, gauss_w_lut, block_hists, block_hist_size)) - return false; - - if (!ocl_normalize_hists(nbins, blockStride.width, blockStride.height, effect_size.height, - effect_size.width, block_hists, (float)L2HysThreshold)) - return false; - - Size blocks_per_win = numPartsWithin(winSize, blockSize, blockStride); - wins_per_img = numPartsWithin(effect_size, winSize, win_stride); - - int descr_size = blocks_per_win.area()*(int)block_hist_size; - int descr_width = (int)block_hist_size*blocks_per_win.width; - - UMat descriptors(wins_per_img.area(), static_cast(blocks_per_win.area() * block_hist_size), CV_32F); - switch (descr_format) - { - case DESCR_FORMAT_ROW_BY_ROW: - if (!ocl_extract_descrs_by_rows(winSize.height, winSize.width, - blockStride.height, blockStride.width, win_stride.height, win_stride.width, effect_size.height, - effect_size.width, block_hists, descriptors, (int)block_hist_size, descr_size, descr_width)) - return false; - break; - case DESCR_FORMAT_COL_BY_COL: - if (!ocl_extract_descrs_by_cols(winSize.height, winSize.width, - blockStride.height, blockStride.width, win_stride.height, win_stride.width, effect_size.height, effect_size.width, - block_hists, descriptors, (int)block_hist_size, descr_size, blocks_per_win.width, blocks_per_win.height)) - return false; - break; - default: - return false; - } - descriptors.reshape(1, (int)descriptors.total()).getMat(ACCESS_READ).copyTo(_descriptors); - return true; - } -#endif //HAVE_OPENCL - - void HOGDescriptor::compute(InputArray _img, std::vector& descriptors, - Size winStride, Size padding, const std::vector& locations) const - { - CV_INSTRUMENT_REGION(); - - if (winStride == Size()) - winStride = cellSize; - Size cacheStride(gcd(winStride.width, blockStride.width), - gcd(winStride.height, blockStride.height)); - - Size imgSize = _img.size(); - - size_t nwindows = locations.size(); - padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); - padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height); - Size paddedImgSize(imgSize.width + padding.width*2, imgSize.height + padding.height*2); - - CV_OCL_RUN(_img.dims() <= 2 && _img.type() == CV_8UC1 && _img.isUMat(), - ocl_compute(_img, winStride, descriptors, DESCR_FORMAT_COL_BY_COL, blockSize, - cellSize, nbins, blockStride, winSize, (float)getWinSigma(), gammaCorrection, L2HysThreshold, signedGradient)) - - Mat img = _img.getMat(); - HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); - - if (!nwindows) - nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); - - const HOGCache::BlockData* blockData = &cache.blockData[0]; - - int nblocks = cache.nblocks.area(); - int blockHistogramSize = cache.blockHistogramSize; - size_t dsize = getDescriptorSize(); - descriptors.resize(dsize*nwindows); - - // for each window - for (size_t i = 0; i < nwindows; i++) - { - float* descriptor = &descriptors[i*dsize]; - - Point pt0; - if (!locations.empty()) - { - pt0 = locations[i]; - if (pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width || - pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height) - continue; - } - else - { - pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding); -// CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0); - } - - for (int j = 0; j < nblocks; j++) - { - const HOGCache::BlockData& bj = blockData[j]; - Point pt = pt0 + bj.imgOffset; - - float* dst = descriptor + bj.histOfs; - const float* src = cache.getBlock(pt, dst); - if (src != dst) - memcpy(dst, src, blockHistogramSize * sizeof(float)); - } - } - } - - void HOGDescriptor::detect(const Mat& img, - std::vector& hits, std::vector& weights, double hitThreshold, - Size winStride, Size padding, const std::vector& locations) const - { - CV_INSTRUMENT_REGION(); - - hits.clear(); - weights.clear(); - if (svmDetector.empty()) - return; - - if (winStride == Size()) - winStride = cellSize; - Size cacheStride(gcd(winStride.width, blockStride.width), - gcd(winStride.height, blockStride.height)); - - size_t nwindows = locations.size(); - padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); - padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height); - Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2); - - HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); - - if (!nwindows) - nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); - - const HOGCache::BlockData* blockData = &cache.blockData[0]; - - int nblocks = cache.nblocks.area(); - int blockHistogramSize = cache.blockHistogramSize; - size_t dsize = getDescriptorSize(); - - double rho = svmDetector.size() > dsize ? svmDetector[dsize] : 0; - std::vector blockHist(blockHistogramSize); - -#if CV_SIMD128 - float partSum[4]; -#endif - - for (size_t i = 0; i < nwindows; i++) - { - Point pt0; - if (!locations.empty()) - { - pt0 = locations[i]; - if (pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width || - pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height) - continue; - } - else - { - pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding); - CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0); - } - double s = rho; - const float* svmVec = &svmDetector[0]; - - int j, k; - for (j = 0; j < nblocks; j++, svmVec += blockHistogramSize) - { - const HOGCache::BlockData& bj = blockData[j]; - Point pt = pt0 + bj.imgOffset; - - const float* vec = cache.getBlock(pt, &blockHist[0]); -#if CV_SIMD128 - v_float32x4 _vec = v_load(vec); - v_float32x4 _svmVec = v_load(svmVec); - v_float32x4 sum = _svmVec * _vec; - - for (k = 4; k <= blockHistogramSize - 4; k += 4) - { - _vec = v_load(vec + k); - _svmVec = v_load(svmVec + k); - - sum += _vec * _svmVec; - } - - v_store(partSum, sum); - double t0 = partSum[0] + partSum[1]; - double t1 = partSum[2] + partSum[3]; - s += t0 + t1; -#else - for (k = 0; k <= blockHistogramSize - 4; k += 4) - s += vec[k]*svmVec[k] + vec[k+1]*svmVec[k+1] + - vec[k+2]*svmVec[k+2] + vec[k+3]*svmVec[k+3]; -#endif - for ( ; k < blockHistogramSize; k++) - s += vec[k]*svmVec[k]; - } - if (s >= hitThreshold) - { - hits.push_back(pt0); - weights.push_back(s); - } - } - } - - void HOGDescriptor::detect(const Mat& img, std::vector& hits, double hitThreshold, - Size winStride, Size padding, const std::vector& locations) const - { - CV_INSTRUMENT_REGION(); - - std::vector weightsV; - detect(img, hits, weightsV, hitThreshold, winStride, padding, locations); - } - - class HOGInvoker : - public ParallelLoopBody - { - public: - HOGInvoker( const HOGDescriptor* _hog, const Mat& _img, - double _hitThreshold, const Size& _winStride, const Size& _padding, - const double* _levelScale, std::vector * _vec, Mutex* _mtx, - std::vector* _weights=0, std::vector* _scales=0 ) - { - hog = _hog; - img = _img; - hitThreshold = _hitThreshold; - winStride = _winStride; - padding = _padding; - levelScale = _levelScale; - vec = _vec; - weights = _weights; - scales = _scales; - mtx = _mtx; - } - - void operator()(const Range& range) const CV_OVERRIDE - { - int i, i1 = range.start, i2 = range.end; - double minScale = i1 > 0 ? levelScale[i1] : i2 > 1 ? levelScale[i1+1] : std::max(img.cols, img.rows); - Size maxSz(cvCeil(img.cols/minScale), cvCeil(img.rows/minScale)); - Mat smallerImgBuf(maxSz, img.type()); - std::vector locations; - std::vector hitsWeights; - - for (i = i1; i < i2; i++) - { - double scale = levelScale[i]; - Size sz(cvRound(img.cols/scale), cvRound(img.rows/scale)); - Mat smallerImg(sz, img.type(), smallerImgBuf.ptr()); - if (sz == img.size()) - smallerImg = Mat(sz, img.type(), img.data, img.step); - else - resize(img, smallerImg, sz, 0, 0, INTER_LINEAR_EXACT); - hog->detect(smallerImg, locations, hitsWeights, hitThreshold, winStride, padding); - Size scaledWinSize = Size(cvRound(hog->winSize.width*scale), cvRound(hog->winSize.height*scale)); - - mtx->lock(); - for (size_t j = 0; j < locations.size(); j++) - { - vec->push_back(Rect(cvRound(locations[j].x*scale), - cvRound(locations[j].y*scale), - scaledWinSize.width, scaledWinSize.height)); - if (scales) - scales->push_back(scale); - } - mtx->unlock(); - - if (weights && (!hitsWeights.empty())) - { - mtx->lock(); - for (size_t j = 0; j < locations.size(); j++) - weights->push_back(hitsWeights[j]); - mtx->unlock(); - } - } - } - - private: - const HOGDescriptor* hog; - Mat img; - double hitThreshold; - Size winStride; - Size padding; - const double* levelScale; - std::vector* vec; - std::vector* weights; - std::vector* scales; - Mutex* mtx; - }; - -#ifdef HAVE_OPENCL - - static bool ocl_classify_hists(int win_height, int win_width, int block_stride_y, int block_stride_x, - int win_stride_y, int win_stride_x, int height, int width, - const UMat& block_hists, UMat detector, - float free_coef, float threshold, UMat& labels, Size descr_size, int block_hist_size) - { - int nthreads; - bool is_cpu = cv::ocl::Device::getDefault().type() == cv::ocl::Device::TYPE_CPU; - cv::String opts; - - ocl::Kernel k; - int idx = 0; - switch (descr_size.width) - { - case 180: - nthreads = 180; - k.create("classify_hists_180_kernel", ocl::objdetect::objdetect_hog_oclsrc, "-D WAVE_SIZE=32"); - if (k.empty()) - return false; - if (is_cpu) - opts = "-D CPU "; - else - opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); - k.create("classify_hists_180_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); - if (k.empty()) - return false; - idx = k.set(idx, descr_size.width); - idx = k.set(idx, descr_size.height); - break; - - case 252: - nthreads = 256; - k.create("classify_hists_252_kernel", ocl::objdetect::objdetect_hog_oclsrc, "-D WAVE_SIZE=32"); - if (k.empty()) - return false; - if (is_cpu) - opts = "-D CPU "; - else - opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); - k.create("classify_hists_252_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); - if (k.empty()) - return false; - idx = k.set(idx, descr_size.width); - idx = k.set(idx, descr_size.height); - break; - - default: - nthreads = 256; - k.create("classify_hists_kernel", ocl::objdetect::objdetect_hog_oclsrc, "-D WAVE_SIZE=32"); - if (k.empty()) - return false; - if (is_cpu) - opts = "-D CPU "; - else - opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); - k.create("classify_hists_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); - if (k.empty()) - return false; - idx = k.set(idx, descr_size.area()); - idx = k.set(idx, descr_size.height); - } - - int win_block_stride_x = win_stride_x / block_stride_x; - int win_block_stride_y = win_stride_y / block_stride_y; - int img_win_width = (width - win_width + win_stride_x) / win_stride_x; - int img_win_height = (height - win_height + win_stride_y) / win_stride_y; - int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x) / - block_stride_x; - - size_t globalThreads[3] = { (size_t)img_win_width * nthreads, (size_t)img_win_height, 1 }; - size_t localThreads[3] = { (size_t)nthreads, 1, 1 }; - - idx = k.set(idx, block_hist_size); - idx = k.set(idx, img_win_width); - idx = k.set(idx, img_block_width); - idx = k.set(idx, win_block_stride_x); - idx = k.set(idx, win_block_stride_y); - idx = k.set(idx, ocl::KernelArg::PtrReadOnly(block_hists)); - idx = k.set(idx, ocl::KernelArg::PtrReadOnly(detector)); - idx = k.set(idx, free_coef); - idx = k.set(idx, threshold); - idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(labels)); - - return k.run(2, globalThreads, localThreads, false); - } - - static bool ocl_detect(InputArray img, std::vector &hits, double hit_threshold, Size win_stride, - const UMat& oclSvmDetector, Size blockSize, Size cellSize, int nbins, Size blockStride, Size winSize, - bool gammaCorrection, double L2HysThreshold, float sigma, float free_coef, bool signedGradient) - { - hits.clear(); - if (oclSvmDetector.empty()) - return false; - - Size imgSize = img.size(); - Size effect_size = imgSize; - UMat grad(imgSize, CV_32FC2); - int qangle_type = ocl::Device::getDefault().isIntel() ? CV_32SC2 : CV_8UC2; - UMat qangle(imgSize, qangle_type); - - const size_t block_hist_size = getBlockHistogramSize(blockSize, cellSize, nbins); - const Size blocks_per_img = numPartsWithin(imgSize, blockSize, blockStride); - UMat block_hists(1, static_cast(block_hist_size * blocks_per_img.area()) + 256, CV_32F); - - Size wins_per_img = numPartsWithin(imgSize, winSize, win_stride); - UMat labels(1, wins_per_img.area(), CV_8U); - - float scale = 1.f / (2.f * sigma * sigma); - Mat gaussian_lut(1, 512, CV_32FC1); - int idx = 0; - for (int i=-8; i<8; i++) - for (int j=-8; j<8; j++) - gaussian_lut.at(idx++) = std::exp(-(j * j + i * i) * scale); - for (int i=-8; i<8; i++) - for (int j=-8; j<8; j++) - gaussian_lut.at(idx++) = (8.f - fabs(j + 0.5f)) * (8.f - fabs(i + 0.5f)) / 64.f; - - if (!ocl_computeGradient(img, grad, qangle, nbins, effect_size, gammaCorrection, signedGradient)) - return false; - - UMat gauss_w_lut; - gaussian_lut.copyTo(gauss_w_lut); - if (!ocl_compute_hists(nbins, blockStride.width, blockStride.height, effect_size.height, - effect_size.width, grad, qangle, gauss_w_lut, block_hists, block_hist_size)) - return false; - - if (!ocl_normalize_hists(nbins, blockStride.width, blockStride.height, effect_size.height, - effect_size.width, block_hists, (float)L2HysThreshold)) - return false; - - Size blocks_per_win = numPartsWithin(winSize, blockSize, blockStride); - - Size descr_size((int)block_hist_size*blocks_per_win.width, blocks_per_win.height); - - if (!ocl_classify_hists(winSize.height, winSize.width, blockStride.height, - blockStride.width, win_stride.height, win_stride.width, - effect_size.height, effect_size.width, block_hists, oclSvmDetector, - free_coef, (float)hit_threshold, labels, descr_size, (int)block_hist_size)) - return false; - - Mat labels_host = labels.getMat(ACCESS_READ); - unsigned char *vec = labels_host.ptr(); - for (int i = 0; i < wins_per_img.area(); i++) - { - int y = i / wins_per_img.width; - int x = i - wins_per_img.width * y; - if (vec[i]) - { - hits.push_back(Point(x * win_stride.width, y * win_stride.height)); - } - } - return true; - } - - static bool ocl_detectMultiScale(InputArray _img, std::vector &found_locations, std::vector& level_scale, - double hit_threshold, Size win_stride, double group_threshold, - const UMat& oclSvmDetector, Size blockSize, Size cellSize, - int nbins, Size blockStride, Size winSize, bool gammaCorrection, - double L2HysThreshold, float sigma, float free_coef, bool signedGradient) - { - std::vector all_candidates; - std::vector locations; - UMat image_scale; - Size imgSize = _img.size(); - image_scale.create(imgSize, _img.type()); - - for (size_t i = 0; i& foundLocations, std::vector& foundWeights, - double hitThreshold, Size winStride, Size padding, - double scale0, double finalThreshold, bool useMeanshiftGrouping) const - { - CV_INSTRUMENT_REGION(); - - double scale = 1.; - int levels = 0; - - Size imgSize = _img.size(); - std::vector levelScale; - for (levels = 0; levels < nlevels; levels++) - { - levelScale.push_back(scale); - if (cvRound(imgSize.width/scale) < winSize.width || - cvRound(imgSize.height/scale) < winSize.height || - scale0 <= 1) - break; - scale *= scale0; - } - levels = std::max(levels, 1); - levelScale.resize(levels); - - if (winStride == Size()) - winStride = blockStride; - - CV_OCL_RUN(_img.dims() <= 2 && _img.type() == CV_8UC1 && scale0 > 1 && winStride.width % blockStride.width == 0 && - winStride.height % blockStride.height == 0 && padding == Size(0,0) && _img.isUMat(), - ocl_detectMultiScale(_img, foundLocations, levelScale, hitThreshold, winStride, finalThreshold, oclSvmDetector, - blockSize, cellSize, nbins, blockStride, winSize, gammaCorrection, L2HysThreshold, (float)getWinSigma(), free_coef, signedGradient)); - - std::vector allCandidates; - std::vector tempScales; - std::vector tempWeights; - std::vector foundScales; - - Mutex mtx; - Mat img = _img.getMat(); - Range range(0, (int)levelScale.size()); - HOGInvoker invoker(this, img, hitThreshold, winStride, padding, &levelScale[0], &allCandidates, &mtx, &tempWeights, &tempScales); - parallel_for_(range, invoker); - - std::copy(tempScales.begin(), tempScales.end(), back_inserter(foundScales)); - foundLocations.clear(); - std::copy(allCandidates.begin(), allCandidates.end(), back_inserter(foundLocations)); - foundWeights.clear(); - std::copy(tempWeights.begin(), tempWeights.end(), back_inserter(foundWeights)); - - if (useMeanshiftGrouping) - groupRectangles_meanshift(foundLocations, foundWeights, foundScales, finalThreshold, winSize); - else - groupRectangles(foundLocations, foundWeights, (int)finalThreshold, 0.2); - clipObjects(imgSize, foundLocations, 0, &foundWeights); - } - - void HOGDescriptor::detectMultiScale(InputArray img, std::vector& foundLocations, - double hitThreshold, Size winStride, Size padding, - double scale0, double finalThreshold, bool useMeanshiftGrouping) const - { - CV_INSTRUMENT_REGION(); - - std::vector foundWeights; - detectMultiScale(img, foundLocations, foundWeights, hitThreshold, winStride, - padding, scale0, finalThreshold, useMeanshiftGrouping); - } - - template struct RTTIImpl - { - public: - static int isInstance(const void* ptr) - { - static _ClsName dummy; - static void* dummyp = &dummy; - union - { - const void* p; - const void** pp; - } a, b; - a.p = dummyp; - b.p = ptr; - return *a.pp == *b.pp; - } - static void release(void** dbptr) - { - if (dbptr && *dbptr) - { - delete (_ClsName*)*dbptr; - *dbptr = 0; - } - } - static void* read(CvFileStorage* fs, CvFileNode* n) - { - FileNode fn(fs, n); - _ClsName* obj = new _ClsName; - if (obj->read(fn)) - return obj; - delete obj; - return 0; - } - - static void write(CvFileStorage* _fs, const char* name, const void* ptr, CvAttrList) - { - if (ptr && _fs) - { - FileStorage fs(_fs, false); - ((const _ClsName*)ptr)->write(fs, String(name)); - } - } - - static void* clone(const void* ptr) - { - if (!ptr) - return 0; - return new _ClsName(*(const _ClsName*)ptr); - } - }; - - typedef RTTIImpl HOGRTTI; - - CvType hog_type( CV_TYPE_NAME_HOG_DESCRIPTOR, HOGRTTI::isInstance, - HOGRTTI::release, HOGRTTI::read, HOGRTTI::write, HOGRTTI::clone); - - std::vector HOGDescriptor::getDefaultPeopleDetector() - { - static const float detector[] = { - 0.05359386f, -0.14721455f, -0.05532170f, 0.05077307f, - 0.11547081f, -0.04268804f, 0.04635834f, -0.05468199f, 0.08232084f, - 0.10424068f, -0.02294518f, 0.01108519f, 0.01378693f, 0.11193510f, - 0.01268418f, 0.08528346f, -0.06309239f, 0.13054633f, 0.08100729f, - -0.05209739f, -0.04315529f, 0.09341384f, 0.11035026f, -0.07596218f, - -0.05517511f, -0.04465296f, 0.02947334f, 0.04555536f, - -3.55954492e-003f, 0.07818956f, 0.07730991f, 0.07890715f, 0.06222893f, - 0.09001380f, -0.03574381f, 0.03414327f, 0.05677258f, -0.04773581f, - 0.03746637f, -0.03521175f, 0.06955440f, -0.03849038f, 0.01052293f, - 0.01736112f, 0.10867710f, 0.08748853f, 3.29739624e-003f, 0.10907028f, - 0.07913758f, 0.10393070f, 0.02091867f, 0.11594022f, 0.13182420f, - 0.09879354f, 0.05362710f, -0.06745391f, -7.01260753e-003f, - 5.24702156e-003f, 0.03236255f, 0.01407916f, 0.02207983f, 0.02537322f, - 0.04547948f, 0.07200756f, 0.03129894f, -0.06274468f, 0.02107014f, - 0.06035208f, 0.08636236f, 4.53164103e-003f, 0.02193363f, 0.02309801f, - 0.05568166f, -0.02645093f, 0.04448695f, 0.02837519f, 0.08975694f, - 0.04461516f, 0.08975355f, 0.07514391f, 0.02306982f, 0.10410084f, - 0.06368385f, 0.05943464f, 4.58420580e-003f, 0.05220337f, 0.06675851f, - 0.08358569f, 0.06712101f, 0.06559004f, -0.03930482f, -9.15936660e-003f, - -0.05897915f, 0.02816453f, 0.05032348f, 0.06780671f, 0.03377650f, - -6.09417039e-004f, -0.01795146f, -0.03083684f, -0.01302475f, - -0.02972313f, 7.88706727e-003f, -0.03525961f, -2.50397739e-003f, - 0.05245084f, 0.11791293f, -0.02167498f, 0.05299332f, 0.06640524f, - 0.05190265f, -8.27316567e-003f, 0.03033127f, 0.05842173f, - -4.01050318e-003f, -6.25105947e-003f, 0.05862958f, -0.02465461f, - 0.05546781f, -0.08228195f, -0.07234028f, 0.04640540f, -0.01308254f, - -0.02506191f, 0.03100746f, -0.04665651f, -0.04591486f, 0.02949927f, - 0.06035462f, 0.02244646f, -0.01698639f, 0.01040041f, 0.01131170f, - 0.05419579f, -0.02130277f, -0.04321722f, -0.03665198f, 0.01126490f, - -0.02606488f, -0.02228328f, -0.02255680f, -0.03427236f, - -7.75165204e-003f, -0.06195229f, 8.21638294e-003f, 0.09535975f, - -0.03709979f, -0.06942501f, 0.14579427f, -0.05448192f, -0.02055904f, - 0.05747357f, 0.02781788f, -0.07077577f, -0.05178314f, -0.10429011f, - -0.11235505f, 0.07529039f, -0.07559302f, -0.08786739f, 0.02983843f, - 0.02667585f, 0.01382199f, -0.01797496f, -0.03141199f, -0.02098101f, - 0.09029204f, 0.04955018f, 0.13718739f, 0.11379953f, 1.80019124e-003f, - -0.04577610f, -1.11108483e-003f, -0.09470536f, -0.11596080f, - 0.04489342f, 0.01784211f, 3.06850672e-003f, 0.10781866f, - 3.36498418e-003f, -0.10842580f, -0.07436839f, -0.10535070f, - -0.01866805f, 0.16057891f, -5.07316366e-003f, -0.04295658f, - -5.90488780e-003f, 8.82003549e-003f, -0.01492646f, -0.05029279f, - -0.12875880f, 8.78831954e-004f, -0.01297184f, -0.07592774f, - -0.02668831f, -6.93787413e-004f, 0.02406698f, -0.01773298f, - -0.03855745f, -0.05877856f, 0.03259695f, 0.12826584f, 0.06292590f, - -4.10733931e-003f, 0.10996531f, 0.01332991f, 0.02088735f, 0.04037504f, - -0.05210760f, 0.07760046f, 0.06399347f, -0.05751930f, -0.10053057f, - 0.07505023f, -0.02139782f, 0.01796176f, 2.34400877e-003f, -0.04208319f, - 0.07355055f, 0.05093350f, -0.02996780f, -0.02219072f, 0.03355330f, - 0.04418742f, -0.05580705f, -0.05037573f, -0.04548179f, 0.01379514f, - 0.02150671f, -0.02194211f, -0.13682702f, 0.05464972f, 0.01608082f, - 0.05309116f, 0.04701022f, 1.33690401e-003f, 0.07575664f, 0.09625306f, - 8.92647635e-003f, -0.02819123f, 0.10866830f, -0.03439325f, - -0.07092371f, -0.06004780f, -0.02712298f, -7.07467366e-003f, - -0.01637020f, 0.01336790f, -0.10313606f, 0.04906582f, -0.05732445f, - -0.02731079f, 0.01042235f, -0.08340668f, 0.03686501f, 0.06108340f, - 0.01322748f, -0.07809529f, 0.03774724f, -0.03413248f, -0.06096525f, - -0.04212124f, -0.07982176f, -1.25973229e-003f, -0.03045501f, - -0.01236493f, -0.06312395f, 0.04789570f, -0.04602066f, 0.08576570f, - 0.02521080f, 0.02988098f, 0.10314583f, 0.07060035f, 0.04520544f, - -0.04426654f, 0.13146530f, 0.08386490f, 0.02164590f, -2.12280243e-003f, - -0.03686353f, -0.02074944f, -0.03829959f, -0.01530596f, 0.02689708f, - 0.11867401f, -0.06043470f, -0.02785023f, -0.04775074f, 0.04878745f, - 0.06350956f, 0.03494788f, 0.01467400f, 1.17890188e-003f, 0.04379614f, - 2.03681854e-003f, -0.03958609f, -0.01072688f, 6.43705716e-003f, - 0.02996500f, -0.03418507f, -0.01960307f, -0.01219154f, - -4.37000440e-003f, -0.02549453f, 0.02646318f, -0.01632513f, - 6.46516960e-003f, -0.01929734f, 4.78711911e-003f, 0.04962371f, - 0.03809111f, 0.07265724f, 0.05758125f, -0.03741554f, 0.01648608f, - -8.45285598e-003f, 0.03996826f, -0.08185477f, 0.02638875f, - -0.04026615f, -0.02744674f, -0.04071517f, 1.05096330e-003f, - -0.04741232f, -0.06733172f, 8.70434940e-003f, -0.02192543f, - 1.35350740e-003f, -0.03056974f, -0.02975521f, -0.02887780f, - -0.01210713f, -0.04828526f, -0.09066251f, -0.09969629f, -0.03665164f, - -8.88111943e-004f, -0.06826669f, -0.01866150f, -0.03627640f, - -0.01408288f, 0.01874239f, -0.02075835f, 0.09145175f, -0.03547291f, - 0.05396780f, 0.04198981f, 0.01301925f, -0.03384354f, -0.12201976f, - 0.06830920f, -0.03715654f, 9.55848210e-003f, 5.05685573e-003f, - 0.05659294f, 3.90764466e-003f, 0.02808490f, -0.05518097f, -0.03711621f, - -0.02835565f, -0.04420464f, -0.01031947f, 0.01883466f, - -8.49525444e-003f, -0.09419250f, -0.01269387f, -0.02133371f, - -0.10190815f, -0.07844430f, 2.43644323e-003f, -4.09610150e-003f, - 0.01202551f, -0.06452291f, -0.10593818f, -0.02464746f, -0.02199699f, - -0.07401930f, 0.07285886f, 8.87513801e-004f, 9.97662079e-003f, - 8.46779719e-003f, 0.03730333f, -0.02905126f, 0.03573337f, -0.04393689f, - -0.12014472f, 0.03176554f, -2.76015815e-003f, 0.10824566f, 0.05090732f, - -3.30179278e-003f, -0.05123822f, 5.04784798e-003f, -0.05664124f, - -5.99415926e-003f, -0.05341901f, -0.01221393f, 0.01291318f, - 9.91760660e-003f, -7.56987557e-003f, -0.06193124f, -2.24549137e-003f, - 0.01987562f, -0.02018840f, -0.06975540f, -0.06601523f, -0.03349112f, - -0.08910118f, -0.03371435f, -0.07406893f, -0.02248047f, -0.06159951f, - 2.77751544e-003f, -0.05723337f, -0.04792468f, 0.07518548f, - 2.77279224e-003f, 0.04211938f, 0.03100502f, 0.05278448f, 0.03954679f, - -0.03006846f, -0.03851741f, -0.02792403f, -0.02875333f, 0.01531280f, - 0.02186953f, -0.01989829f, 2.50679464e-003f, -0.10258728f, - -0.04785743f, -0.02887216f, 3.85063468e-003f, 0.01112236f, - 8.29218887e-003f, -0.04822981f, -0.04503597f, -0.03713100f, - -0.06988008f, -0.11002295f, -2.69209221e-003f, 1.85383670e-003f, - -0.05921049f, -0.06105053f, -0.08458050f, -0.04527602f, - 8.90329306e-004f, -0.05875023f, -2.68602883e-003f, -0.01591195f, - 0.03631859f, 0.05493166f, 0.07300330f, 5.53333294e-003f, 0.06400407f, - 0.01847740f, -5.76280477e-003f, -0.03210877f, 4.25160583e-003f, - 0.01166520f, -1.44864211e-003f, 0.02253744f, -0.03367080f, 0.06983195f, - -4.22323542e-003f, -8.89401045e-003f, -0.07943393f, 0.05199728f, - 0.06065201f, 0.04133492f, 1.44032843e-003f, -0.09585235f, -0.03964731f, - 0.04232114f, 0.01750465f, -0.04487902f, -7.59733608e-003f, 0.02011171f, - 0.04673622f, 0.09011173f, -0.07869188f, -0.04682482f, -0.05080139f, - -3.99383716e-003f, -0.05346331f, 0.01085723f, -0.03599333f, - -0.07097908f, 0.03551549f, 0.02680387f, 0.03471529f, 0.01790393f, - 0.05471273f, 9.62048303e-003f, -0.03180215f, 0.05864431f, 0.02330614f, - 0.01633144f, -0.05616681f, -0.10245429f, -0.08302189f, 0.07291322f, - -0.01972590f, -0.02619633f, -0.02485327f, -0.04627592f, - 1.48853404e-003f, 0.05514185f, -0.01270860f, -0.01948900f, 0.06373586f, - 0.05002292f, -0.03009798f, 8.76216311e-003f, -0.02474238f, - -0.05504891f, 1.74034527e-003f, -0.03333667f, 0.01524987f, 0.11663762f, - -1.32344989e-003f, -0.06608453f, 0.05687166f, -6.89525274e-004f, - -0.04402352f, 0.09450210f, -0.04222684f, -0.05360983f, 0.01779531f, - 0.02561388f, -0.11075410f, -8.77790991e-003f, -0.01099504f, - -0.10380266f, 0.03103457f, -0.02105741f, -0.07371717f, 0.05146710f, - 0.10581432f, -0.08617968f, -0.02892107f, 0.01092199f, 0.14551543f, - -2.24320893e-003f, -0.05818033f, -0.07390742f, 0.05701261f, - 0.12937020f, -0.04986651f, 0.10182415f, 0.05028650f, 0.12515625f, - 0.09175041f, 0.06404983f, 0.01523394f, 0.09460562f, 0.06106631f, - -0.14266998f, -0.02926703f, 0.02762171f, 0.02164151f, - -9.58488265e-004f, -0.04231362f, -0.09866509f, 0.04322244f, - 0.05872034f, -0.04838847f, 0.06319253f, 0.02443798f, -0.03606876f, - 9.38737206e-003f, 0.04289991f, -0.01027411f, 0.08156885f, 0.08751175f, - -0.13191354f, 8.16054735e-003f, -0.01452161f, 0.02952677f, 0.03615945f, - -2.09128903e-003f, 0.02246693f, 0.09623287f, 0.09412123f, -0.02924758f, - -0.07815186f, -0.02203079f, -2.02566991e-003f, 0.01094733f, - -0.01442332f, 0.02838561f, 0.11882371f, 7.28798332e-003f, -0.10345965f, - 0.07561217f, -0.02049661f, 4.44177445e-003f, 0.01609347f, -0.04893158f, - -0.08758243f, -7.67420698e-003f, 0.08862378f, 0.06098121f, 0.06565887f, - 7.32981879e-003f, 0.03558407f, -0.03874352f, -0.02490055f, - -0.06771075f, 0.09939223f, -0.01066077f, 0.01382995f, -0.07289080f, - 7.47184316e-003f, 0.10621431f, -0.02878659f, 0.02383525f, -0.03274646f, - 0.02137008f, 0.03837290f, 0.02450992f, -0.04296818f, -0.02895143f, - 0.05327370f, 0.01499020f, 0.04998732f, 0.12938657f, 0.09391870f, - 0.04292390f, -0.03359194f, -0.06809492f, 0.01125796f, 0.17290455f, - -0.03430733f, -0.06255233f, -0.01813114f, 0.11726857f, -0.06127599f, - -0.08677909f, -0.03429872f, 0.04684938f, 0.08161420f, 0.03538774f, - 0.01833884f, 0.11321855f, 0.03261845f, -0.04826299f, 0.01752407f, - -0.01796414f, -0.10464549f, -3.30041884e-003f, 2.29343961e-004f, - 0.01457292f, -0.02132982f, -0.02602923f, -9.87351313e-003f, - 0.04273872f, -0.02103316f, -0.07994065f, 0.02614958f, -0.02111666f, - -0.06964913f, -0.13453490f, -0.06861878f, -6.09341264e-003f, - 0.08251446f, 0.15612499f, 2.46531400e-003f, 8.88424646e-003f, - -0.04152999f, 0.02054853f, 0.05277953f, -0.03087788f, 0.02817579f, - 0.13939077f, 0.07641046f, -0.03627627f, -0.03015098f, -0.04041540f, - -0.01360690f, -0.06227205f, -0.02738223f, 0.13577610f, 0.15235767f, - -0.05392922f, -0.11175954f, 0.02157129f, 0.01146481f, -0.05264937f, - -0.06595174f, -0.02749175f, 0.11812254f, 0.17404149f, -0.06137035f, - -0.11003478f, -0.01351621f, -0.01745916f, -0.08577441f, -0.04469909f, - -0.06106115f, 0.10559758f, 0.20806813f, -0.09174948f, 7.09621934e-004f, - 0.03579374f, 0.07215115f, 0.02221742f, 0.01827742f, -7.90785067e-003f, - 0.01489554f, 0.14519960f, -0.06425831f, 0.02990399f, -1.80181325e-003f, - -0.01401528f, -0.04171134f, -3.70530109e-003f, -0.09090481f, - 0.09520713f, 0.08845516f, -0.02651753f, -0.03016730f, 0.02562448f, - 0.03563816f, -0.03817881f, 0.01433385f, 0.02256983f, 0.02872120f, - 0.01001934f, -0.06332260f, 0.04338406f, 0.07001807f, -0.04705722f, - -0.07318907f, 0.02630457f, 0.03106382f, 0.06648342f, 0.10913180f, - -0.01630815f, 0.02910308f, 0.02895109f, 0.08040254f, 0.06969310f, - 0.06797734f, 6.08639978e-003f, 4.16588830e-003f, 0.08926726f, - -0.03123648f, 0.02700146f, 0.01168734f, -0.01631594f, 4.61015804e-003f, - 8.51359498e-003f, -0.03544224f, 0.03571994f, 4.29766066e-003f, - -0.01970077f, -8.79793242e-003f, 0.09607988f, 0.01544222f, - -0.03923707f, 0.07308586f, 0.06061262f, 1.31683104e-004f, - -7.98222050e-003f, 0.02399261f, -0.06084389f, -0.02743429f, - -0.05475523f, -0.04131311f, 0.03559756f, 0.03055342f, 0.02981433f, - 0.14860515f, 0.01766787f, 0.02945257f, 0.04898238f, 0.01026922f, - 0.02811658f, 0.08267091f, 0.02732154f, -0.01237693f, 0.11760156f, - 0.03802063f, -0.03309754f, 5.24957618e-003f, -0.02460510f, 0.02691451f, - 0.05399988f, -0.10133506f, 0.06385437f, -0.01818005f, 0.02259503f, - 0.03573135f, 0.01042848f, -0.04153402f, -0.04043029f, 0.01643575f, - 0.08326677f, 4.61383024e-004f, -0.05308095f, -0.08536223f, - -1.61011645e-003f, -0.02163720f, -0.01783352f, 0.03859637f, - 0.08498885f, -0.01725216f, 0.08625131f, 0.10995087f, 0.09177644f, - 0.08498347f, 0.07646490f, 0.05580502f, 0.02693516f, 0.09996913f, - 0.09070327f, 0.06667200f, 0.05873008f, -0.02247842f, 0.07772321f, - 0.12408436f, 0.12629253f, -8.41997913e-004f, 0.01477783f, 0.09165990f, - -2.98401713e-003f, -0.06466447f, -0.07057302f, 2.09516948e-004f, - 0.02210209f, -0.02158809f, -0.08602506f, -0.02284836f, - 4.01876355e-003f, 9.56660323e-003f, -0.02073978f, -0.04635138f, - -7.59423291e-003f, -0.01377393f, -0.04559359f, -0.13284740f, - -0.08671406f, -0.03654395f, 0.01142869f, 0.03287891f, -0.04392983f, - 0.06142959f, 0.17710890f, 0.10385257f, 0.01329137f, 0.10067633f, - 0.12450829f, -0.04476709f, 0.09049144f, 0.04589312f, 0.11167907f, - 0.08587538f, 0.04767583f, 1.67188141e-003f, 0.02359802f, -0.03808852f, - 0.03126272f, -0.01919029f, -0.05698918f, -0.02365112f, -0.06519032f, - -0.05599358f, -0.07097308f, -0.03301812f, -0.04719102f, -0.02566297f, - 0.01324074f, -0.09230672f, -0.05518232f, -0.04712864f, -0.03380903f, - -0.06719479f, 0.01183908f, -0.09326738f, 0.01642865f, 0.03789867f, - -6.61567831e-003f, 0.07796386f, 0.07246574f, 0.04706347f, -0.02523437f, - -0.01696830f, -0.08068866f, 0.06030888f, 0.10527060f, -0.06611756f, - 0.02977346f, 0.02621830f, 0.01913855f, -0.08479366f, -0.06322418f, - -0.13570616f, -0.07644490f, 9.31900274e-003f, -0.08095149f, - -0.10197903f, -0.05204025f, 0.01413151f, -0.07800411f, -0.01885122f, - -0.07509381f, -0.10136326f, -0.05212355f, -0.09944065f, - -1.33606605e-003f, -0.06342617f, -0.04178550f, -0.12373723f, - -0.02832736f, -0.06057501f, 0.05830070f, 0.07604282f, -0.06462587f, - 8.02447461e-003f, 0.11580125f, 0.12332212f, 0.01978462f, - -2.72378162e-003f, 0.05850752f, -0.04674481f, 0.05148062f, - -2.62542837e-003f, 0.11253355f, 0.09893716f, 0.09785093f, -0.04659257f, - -0.01102429f, -0.07002308f, 0.03088913f, -0.02565549f, -0.07671449f, - 3.17443861e-003f, -0.10783514f, -0.02314270f, -0.11089555f, - -0.01024768f, 0.03116021f, -0.04964825f, 0.02281825f, 5.50005678e-003f, - -0.08427856f, -0.14685495f, -0.07719755f, -0.13342668f, -0.04525511f, - -0.09914210f, 0.02588859f, 0.03469279f, 0.04664020f, 0.11688190f, - 0.09647275f, 0.10857815f, -0.01448726f, 0.04299758f, -0.06763151f, - 1.33257592e-003f, 0.14331576f, 0.07574340f, 0.09166205f, 0.05674926f, - 0.11325553f, -0.01106494f, 0.02062161f, -0.11484840f, -0.07492137f, - -0.02864293f, -0.01275638f, -0.06946032f, -0.10101652f, -0.04113498f, - -0.02214783f, -0.01273942f, -0.07480393f, -0.10556041f, -0.07622112f, - -0.09988393f, -0.11453961f, -0.12073903f, -0.09412795f, -0.07146588f, - -0.04054537f, -0.06127083f, 0.04221122f, 0.07688113f, 0.04099256f, - 0.12663734f, 0.14683802f, 0.21761774f, 0.12525328f, 0.18431792f, - -1.66402373e-003f, 2.37777247e-003f, 0.01445475f, 0.03509416f, - 0.02654697f, 0.01716739f, 0.05374011f, 0.02944174f, 0.11323927f, - -0.01485456f, -0.01611330f, -1.85554172e-003f, -0.01708549f, - -0.05435753f, -0.05302101f, 0.05260378f, -0.03582945f, - -3.42867890e-004f, 1.36076682e-003f, -0.04436073f, -0.04228432f, - 0.03281291f, -0.05480836f, -0.10197772f, -0.07206279f, -0.10741059f, - -0.02366946f, 0.10278475f, -2.74783419e-003f, -0.03242477f, - 0.02308955f, 0.02835869f, 0.10348799f, 0.19580358f, 0.10252027f, - 0.08039929f, 0.05525554f, -0.13250865f, -0.14395352f, 3.13586881e-003f, - -0.03387071f, 8.94669443e-003f, 0.05406157f, -4.97324532e-003f, - -0.01189114f, 2.82919413e-004f, -0.03901557f, -0.04898705f, - 0.02164520f, -0.01382906f, -0.01850416f, 0.01869347f, -0.02450060f, - 0.02291678f, 0.08196463f, 0.03309153f, -0.10629974f, 0.02473924f, - 0.05344394f, -0.02404823f, -0.03243643f, -5.55244600e-003f, - -0.08009996f, 0.02811539f, 0.04235742f, 0.01859004f, 0.04902123f, - -0.01438252f, -0.01526853f, 0.02044195f, -0.05008660f, 0.04244113f, - 0.07611816f, 0.04950470f, -0.06020549f, -4.26026015e-003f, 0.13133512f, - -0.01438738f, -0.01958807f, -0.04044152f, -0.12425045f, - 2.84353318e-003f, -0.05042776f, -0.09121484f, 7.34345755e-003f, - 0.09388847f, 0.11800314f, 4.72295098e-003f, 4.44378285e-003f, - -0.07984917f, -0.03613737f, 0.04490915f, -0.02246483f, 0.04681071f, - 0.05240871f, 0.02157206f, -0.04603431f, -0.01197929f, -0.02748779f, - 0.13621049f, 0.08812155f, -0.07802048f, 4.86458559e-003f, -0.01598836f, - 0.01024450f, -0.03463517f, -0.02304239f, -0.08692665f, 0.06655128f, - 0.05785803f, -0.12640759f, 0.02307472f, 0.07337402f, 0.07525434f, - 0.04943763f, -0.02241034f, -0.09978238f, 0.14487994f, -0.06570521f, - -0.07855482f, 0.02830222f, -5.29603509e-004f, -0.04669895f, - -0.11822784f, -0.12246452f, -0.15365660f, -0.02969127f, 0.08078201f, - 0.13512598f, 0.11505685f, 0.04740673f, 0.01376022f, -0.05852978f, - -0.01537809f, -0.05541119f, 0.02491065f, -0.02870786f, 0.02760978f, - 0.23836176f, 0.22347429f, 0.10306466f, -0.06919070f, -0.10132039f, - -0.20198342f, -0.05040560f, 0.27163076f, 0.36987007f, 0.34540465f, - 0.29095781f, 0.05649706f, 0.04125737f, 0.07505883f, -0.02737836f, - -8.43431335e-003f, 0.07368195f, 0.01653876f, -0.09402955f, - -0.09574359f, 0.01474337f, -0.07128561f, -0.03460737f, 0.11438941f, - 0.13752601f, -0.06385452f, -0.06310338f, 8.19548313e-003f, 0.11622470f, - 5.05133113e-003f, -0.07602754f, 0.06695660f, 0.25723928f, 0.09037900f, - 0.28826267f, 0.13165380f, -0.05312614f, -0.02137198f, -0.03442232f, - -0.06255679f, 0.03899667f, 0.18391028f, 0.26016650f, 0.03374462f, - 0.01860465f, 0.19077586f, 0.18160543f, 3.43634398e-003f, -0.03036782f, - 0.19683038f, 0.35378191f, 0.24968483f, -0.03222649f, 0.28972381f, - 0.43091634f, 0.30778357f, 0.02335266f, -0.09877399f, -6.85245218e-003f, - 0.08945240f, -0.08150686f, 0.02792493f, 0.24806842f, 0.17338486f, - 0.06231801f, -0.10432383f, -0.16653322f, -0.13197899f, -0.08531576f, - -0.19271527f, -0.13536365f, 0.22240199f, 0.39219588f, 0.26597717f, - -0.01231649f, 0.01016179f, 0.13379875f, 0.12018334f, -0.04852953f, - -0.07915270f, 0.07036012f, 3.87723115e-003f, -0.06126805f, - -0.15015170f, -0.11406515f, -0.08556531f, -0.07429333f, -0.16115491f, - 0.13214062f, 0.25691369f, 0.05697750f, 0.06861912f, -6.02903729e-003f, - -7.94562511e-003f, 0.04799571f, 0.06695165f, -0.01926842f, 0.06206308f, - 0.13450983f, -0.06381495f, -2.98370165e-003f, -0.03482971f, - 7.53991678e-003f, 0.03895611f, 0.11464261f, 0.01669971f, - 8.27818643e-003f, -7.49160210e-003f, -0.11712562f, -0.10650621f, - -0.10353880f, -0.04994106f, -7.65618810e-004f, 0.03023767f, - -0.04759270f, -0.07302686f, -0.05825012f, -0.13156348f, -0.10639747f, - -0.19393684f, -0.09973683f, -0.07918908f, 4.63177625e-004f, - -6.61382044e-004f, 0.15853868f, 0.08561199f, -0.07660093f, - -0.08015265f, -0.06164073f, 0.01882577f, -7.29908410e-004f, - 0.06840892f, 0.03843764f, 0.20274927f, 0.22028814f, -5.26101235e-003f, - 0.01452435f, -0.06331623f, 0.02865064f, 0.05673740f, 0.12171564f, - 0.03837196f, 0.03555467f, -0.02662914f, -0.10280123f, -0.06526285f, - -0.11066351f, -0.08988424f, -0.10103678f, 8.10526591e-003f, - 5.95238712e-003f, 0.02617721f, -0.01705742f, -0.10897956f, - -0.08004991f, -0.11271993f, -0.06185647f, -0.06103712f, 0.01597041f, - -0.05923606f, 0.09410726f, 0.22858568f, 0.03263380f, 0.06772990f, - -0.09003516f, 0.01017870f, 0.01931688f, 0.08628357f, -0.01430009f, - 0.10954945f, 0.16612452f, -0.02434544f, -0.03310068f, -0.04236627f, - 0.01212392f, -6.15046406e-003f, 0.06954194f, 0.03015283f, 0.01787957f, - 0.02781667f, -0.05561153f, -8.96244217e-003f, -0.04971489f, - 0.07510284f, 0.01775282f, 0.05889897f, -0.07981427f, 0.03647643f, - -3.73833324e-003f, -0.08894575f, -0.06429435f, -0.08068276f, - 0.03567704f, -0.07131936f, -7.21910037e-003f, -0.09566668f, - 0.17886090f, 0.14911725f, 0.02070032f, -0.05017120f, -0.04992622f, - 0.01570143f, -0.09906903f, 0.06456193f, 0.15329507f, 0.18820767f, - 0.11689861f, -0.01178513f, -0.02225163f, -0.01905318f, 0.10271224f, - -7.27029052e-003f, 0.11664233f, 0.14796902f, 0.07771893f, 0.02400013f, - -0.05361797f, -0.01972888f, 0.01376177f, 0.06740040f, -0.06525395f, - 0.05726178f, -0.02404981f, -0.14018567f, -0.02074987f, -0.04621970f, - -0.04688627f, -0.01842059f, 0.07722727f, -0.04852883f, 0.01529004f, - -0.19639495f, 0.10817073f, 0.03795860f, -0.09435206f, -0.07984378f, - -0.03383440f, 0.11081333f, 0.02237366f, 0.12703256f, 0.21613893f, - 0.02918790f, 4.66472283e-003f, -0.10274266f, -0.04854131f, - -3.46305710e-003f, 0.08652268f, 0.02251546f, 0.09636052f, 0.17180754f, - -0.09272388f, 4.59174305e-004f, -0.11723048f, -0.12210111f, - -0.15547538f, 0.07218186f, -0.05297846f, 0.03779940f, 0.05150875f, - -0.03802310f, 0.03870645f, -0.15250699f, -0.08696499f, -0.02021560f, - 0.04118926f, -0.15177974f, 0.01577647f, 0.10249301f, 7.50041893e-003f, - 0.01721806f, -0.06828983f, -0.02397596f, -0.06598977f, -0.04317593f, - -0.08064980f, 6.66632550e-003f, 0.03333484f, 0.07093620f, 0.08231064f, - -0.06577903f, -0.06698844f, -0.06984019f, -0.06508023f, -0.14145090f, - -0.02393239f, 0.06485303f, 8.83263443e-003f, 0.09251080f, -0.07557579f, - -0.05067699f, -0.09798748f, -0.06703258f, -0.14056294f, 0.03245994f, - 0.12554143f, 0.01761621f, 0.12980327f, -0.04081950f, -0.11906909f, - -0.14813015f, -0.08376863f, -0.12200681f, 0.04988137f, 0.05424247f, - -3.90952639e-003f, 0.03255733f, -0.12717837f, -0.07461493f, - -0.05703964f, -0.01736189f, -0.08026433f, -0.05433894f, -0.01719359f, - 0.02886275f, 0.01772653f, -0.09163518f, 3.57789593e-003f, -0.10129993f, - -0.02653764f, -0.08131415f, -0.03847986f, -7.62157550e-004f, - 0.06486648f, 0.19675669f, -0.04919156f, -0.07059129f, -0.04857785f, - -0.01042383f, -0.08328653f, 0.03660302f, -0.03696846f, 0.04969259f, - 0.08241162f, -0.12514858f, -0.06122676f, -0.03750202f, - 6.52989605e-003f, -0.10247213f, 0.02568346f, 4.51781414e-003f, - -0.03734229f, -0.01131264f, -0.05412074f, 8.89345480e-004f, - -0.12388977f, -0.05959237f, -0.12418608f, -0.06151643f, -0.07310260f, - 0.02441575f, 0.07023528f, -0.07548289f, -7.57147965e-004f, - -0.09061348f, -0.08112976f, -0.06920306f, 9.54394229e-003f, - -0.01219902f, 1.21273217e-003f, -8.88989680e-003f, -0.08309301f, - -0.04552661f, -0.10739882f, -0.05691034f, -0.13928030f, 0.09027749f, - 0.15123098f, 0.03175976f, 0.17763577f, 3.29913251e-004f, 0.05151888f, - -0.09844074f, -0.09475287f, -0.08571247f, 0.16241577f, 0.19336018f, - 8.57454538e-003f, 0.11474732f, -0.01493934f, 0.03352379f, -0.08966240f, - -0.02322310f, 0.02663568f, 0.05448750f, -0.03536883f, -0.07210463f, - -0.06807277f, -0.03121621f, -0.05932408f, -0.17282860f, -0.15873498f, - -0.04956378f, 0.01603377f, -0.12385946f, 0.13878587f, 0.21468069f, - 0.13510075f, 0.20992437f, 0.08845878f, 0.08104013f, 0.03754176f, - 0.12173114f, 0.11103114f, 0.10643122f, 0.13941477f, 0.11640384f, - 0.14786847f, 0.01218238f, 0.01160753f, 0.03547940f, 0.08794311f, - -0.01695384f, -0.07692261f, -0.08236158f, 6.79194089e-003f, - -0.02458403f, 0.13022894f, 0.10953187f, 0.09857773f, 0.04735930f, - -0.04353498f, -0.15173385f, -0.17904443f, -0.10450364f, -0.13418166f, - -0.06633098f, -0.03170381f, -0.06839000f, -0.11350126f, -0.06983913f, - 0.19083543f, 0.17604128f, 0.07730632f, 0.10022651f, 0.36428109f, - 0.28291923f, 0.12688625f, 0.15942036f, 0.14064661f, -0.11201853f, - -0.13969108f, -0.09088077f, -0.14107047f, 0.05117374f, - -2.63348082e-003f, -0.10794610f, -0.09715455f, -0.05284977f, - 0.01565668f, 0.05031200f, 0.07021113f, -0.02963028f, 0.01766960f, - 0.08333644f, -0.03211382f, 4.90096770e-003f, 0.05186674f, -0.05045737f, - -0.09624767f, -0.02525997f, 0.06916669f, 0.01213916f, 0.05333899f, - -0.03443280f, -0.10055527f, -0.06291115f, 5.42851724e-003f, - -6.30360236e-003f, 0.02270257f, -0.01769792f, 0.03273688f, 0.07746078f, - 7.77099328e-003f, 0.05041346f, 0.01648103f, -0.02321534f, -0.09930186f, - -0.02293853f, 0.02034990f, -0.08324204f, 0.08510064f, -0.03732836f, - -0.06465405f, -0.06086946f, 0.13680504f, -0.11469388f, -0.03896406f, - -0.07142810f, 2.67581246e-003f, -0.03639632f, -0.09849060f, - -0.11014334f, 0.17489147f, 0.17610909f, -0.16091567f, -0.07248894f, - 0.01567141f, 0.23742996f, 0.07552249f, -0.06270349f, -0.07303379f, - 0.25442186f, 0.16903116f, -0.08168741f, -0.05913896f, -0.03954096f, - 6.81776879e-003f, -0.05615319f, -0.07303037f, -0.12176382f, - 0.12385108f, 0.22084464f, -0.05543206f, -0.03310431f, 0.05731593f, - 0.19481890f, 0.04016430f, -0.06480758f, -0.12353460f, 0.18733442f, - -0.09631214f, -0.11192076f, 0.12404587f, 0.15671748f, 0.19256128f, - 0.10895617f, 0.03391477f, -0.13032004f, -0.05626907f, -0.09025607f, - 0.23485197f, 0.27812332f, 0.26725492f, 0.07255980f, 0.16565137f, - 0.22388470f, 0.07441066f, -0.21003133f, -0.08075339f, -0.15031935f, - 0.07023834f, 0.10872041f, 0.18156518f, 0.20037253f, 0.13571967f, - -0.11915682f, -0.11131983f, -0.18878011f, 0.06074620f, 0.20578890f, - 0.12413109f, 0.03930207f, 0.29176015f, 0.29502738f, 0.27856228f, - -0.01803601f, 0.16646385f, 0.19268319f, 0.01900682f, 0.06026287f, - 2.35868432e-003f, 0.01558199f, 0.02707230f, 0.11383014f, 0.12103992f, - 0.03907350f, 0.04637353f, 0.09020995f, 0.11919726f, -3.63007211e-003f, - 0.02220155f, 0.10336831f, 0.17351882f, 0.12259731f, 0.18983354f, - 0.15736865f, 0.01160725f, -0.01690723f, -9.69582412e-004f, 0.07213813f, - 0.01161613f, 0.17864859f, 0.24486147f, 0.18208991f, 0.20177495f, - 0.05972528f, -8.93934630e-003f, -0.02316955f, 0.14436610f, 0.14114498f, - 0.05520950f, 0.06353590f, -0.19124921f, 0.10174713f, 0.29414919f, - 0.26448128f, 0.09344960f, 0.15284036f, 0.19797507f, 0.11369792f, - -0.12722753f, -0.21396367f, -0.02008235f, -0.06566695f, -0.01662150f, - -0.03937003f, 0.04778343f, 0.05017274f, -0.02299062f, -0.20208496f, - -0.06395898f, 0.13721776f, 0.22544557f, 0.14888357f, 0.08687132f, - 0.27088094f, 0.32206613f, 0.09782200f, -0.18523243f, -0.17232181f, - -0.01041531f, 0.04008654f, 0.04199702f, -0.08081299f, -0.03755421f, - -0.04809646f, -0.05222081f, -0.21709201f, -0.06622940f, 0.02945281f, - -0.04600435f, -0.05256077f, -0.08432942f, 0.02848100f, 0.03490564f, - 8.28621630e-003f, -0.11051246f, -0.11210597f, -0.01998289f, - -0.05369405f, -0.08869293f, -0.18799506f, -0.05436598f, -0.05011634f, - -0.05419716f, -0.06151857f, -0.10827805f, 0.04346735f, 0.04016083f, - 0.01520820f, -0.12173316f, -0.04880285f, -0.01101406f, 0.03250847f, - -0.06009551f, -0.03082932f, -0.02295134f, -0.06856834f, -0.08775249f, - -0.23793389f, -0.09174541f, -0.05538322f, -0.04321031f, -0.11874759f, - -0.04221844f, -0.06070468f, 0.01194489f, 0.02608565f, -0.03892140f, - -0.01643151f, -0.02602034f, -0.01305472f, 0.03920100f, -0.06514261f, - 0.01126918f, -6.27710763e-003f, -0.02720047f, -0.11133634f, - 0.03300330f, 0.02398472f, 0.04079665f, -0.10564448f, 0.05966159f, - 0.01195221f, -0.03179441f, -0.01692590f, -0.06177841f, 0.01841576f, - -5.51078189e-003f, -0.06821765f, -0.03191888f, -0.09545476f, - 0.03030550f, -0.04896152f, -0.02914624f, -0.13283344f, -0.04783419f, - 6.07836898e-003f, -0.01449538f, -0.13358212f, -0.09687774f, - -0.02813793f, 0.01213498f, 0.06650011f, -0.02039067f, 0.13356198f, - 0.05986415f, -9.12760664e-003f, -0.18780160f, -0.11992817f, - -0.06342237f, 0.01229534f, 0.07143231f, 0.10713009f, 0.11085765f, - 0.06569190f, -0.02956399f, -0.16288325f, -0.13993549f, -0.01292515f, - 0.03833013f, 0.09130384f, -0.05086257f, 0.05617329f, -0.03896667f, - -0.06282311f, -0.11490010f, -0.14264110f, -0.04530499f, 0.01598189f, - 0.09167797f, 0.08663294f, 0.04885277f, -0.05741219f, -0.07565769f, - -0.17136464f, -0.02619422f, -0.02477579f, 0.02679587f, 0.11621952f, - 0.08788391f, 0.15520640f, 0.04709549f, 0.04504483f, -0.10214074f, - -0.12293372f, -0.04820546f, -0.05484834f, 0.05473754f, 0.07346445f, - 0.05577277f, -0.08209965f, 0.03462975f, -0.20962234f, -0.09324598f, - 3.79481679e-003f, 0.03617633f, 0.16742408f, 0.07058107f, 0.10204960f, - -0.06795346f, 3.22807301e-003f, -0.12589309f, -0.17496960f, - 0.02078314f, -0.07694324f, 0.12184640f, 0.08997164f, 0.04793497f, - -0.11383379f, -0.08046359f, -0.25716835f, -0.08080962f, - 6.80711539e-003f, -0.02930280f, -3.04938294e-003f, -0.11106286f, - -0.04628860f, -0.07821649f, 7.70127494e-003f, -0.10247706f, - 1.21042714e-003f, 0.20573859f, -0.03241005f, 8.42972286e-003f, - 0.01946464f, -0.01197973f, -0.14579976f, 0.04233614f, - -4.14096704e-003f, -0.06866436f, -0.02431862f, -0.13529138f, - 1.25891645e-003f, -0.11425111f, -0.04303651f, -0.01694815f, - 0.05720210f, -0.16040207f, 0.02772896f, 0.05498345f, -0.15010567f, - 0.01450866f, 0.02350303f, -0.04301004f, -0.04951802f, 0.21702233f, - -0.03159155f, -0.01963303f, 0.18232647f, -0.03263875f, - -2.88476888e-003f, 0.01587562f, -1.94303901e-003f, -0.07789494f, - 0.04674156f, -6.25576358e-003f, 0.08925962f, 0.21353747f, 0.01254677f, - -0.06999976f, -0.05931328f, -0.01884327f, -0.04306272f, 0.11794136f, - 0.03842728f, -0.03907030f, 0.05636114f, -0.09766009f, -0.02104000f, - 8.72711372e-003f, -0.02736877f, -0.05112274f, 0.16996814f, 0.02955785f, - 0.02094014f, 0.08414304f, -0.03335762f, -0.03617457f, -0.05808248f, - -0.08872101f, 0.02927705f, 0.27077839f, 0.06075108f, 0.07478261f, - 0.15282831f, -0.03908454f, -0.05101782f, -9.51998029e-003f, - -0.03272416f, -0.08735625f, 0.07633440f, -0.07185312f, 0.13841286f, - 0.07812646f, -0.12901451f, -0.05488589f, -0.05644578f, -0.03290703f, - -0.11184757f, 0.03751570f, -0.05978153f, -0.09155276f, 0.05657315f, - -0.04328186f, -0.03047933f, -0.01413135f, -0.10181040f, -0.01384013f, - 0.20132534f, -0.01536873f, -0.07641169f, 0.05906778f, -0.07833145f, - -0.01523801f, -0.07502609f, -0.09461885f, -0.15013233f, 0.16050665f, - 0.09021381f, 0.08473236f, 0.03386267f, -0.09147339f, -0.09170618f, - -0.08498498f, -0.05119187f, -0.10431040f, 0.01041618f, -0.03064913f, - 0.09340212f, 0.06448522f, -0.03881054f, -0.04985436f, -0.14794017f, - -0.05200112f, -0.02144495f, 0.04000821f, 0.12420804f, -0.01851651f, - -0.04116732f, -0.11951703f, -0.04879033f, -0.08722515f, -0.08454733f, - -0.10549165f, 0.11251976f, 0.10766345f, 0.19201984f, 0.06128913f, - -0.02734615f, -0.08834923f, -0.16999826f, -0.03548348f, - -5.36092324e-003f, 0.08297954f, 0.07226378f, 0.04194529f, 0.04668673f, - 8.73902347e-003f, 0.06980139f, 0.05652480f, 0.05879445f, 0.02477076f, - 0.02451423f, 0.12433673f, 0.05600227f, 0.06886370f, 0.03863076f, - 0.07459056f, 0.02264139f, 0.01495469f, 0.06344220f, 0.06945208f, - 0.02931899f, 0.11719371f, 0.04527427f, 0.03248192f, 2.08271481e-003f, - 0.02044626f, 0.11403449f, 0.04303892f, 0.06444661f, 0.04959024f, - 0.08174094f, 0.09240247f, 0.04894639f, 0.02252937f, -0.01652530f, - 0.07587013f, 0.06064249f, 0.13954395f, 0.02772832f, 0.07093039f, - 0.08501238f, 0.01701301f, 0.09055722f, 0.33421436f, 0.20163782f, - 0.09821030f, 0.07951369f, 0.08695120f, -0.12757730f, -0.13865978f, - -0.06610068f, -0.10985506f, 0.03406816f, -0.01116336f, -0.07281768f, - -0.13525715f, -0.12844718f, 0.08956250f, 0.09171610f, 0.10092317f, - 0.23385370f, 0.34489515f, 0.09901748f, 0.02002922f, 0.12335990f, - 0.07606190f, -0.14899330f, -0.15634622f, -0.06494618f, -0.01760547f, - 0.03404277f, -0.13208845f, -0.12101169f, -0.18294574f, -0.16560709f, - 0.02183887f, -0.02752613f, 0.01813638f, 0.02000757f, 0.01319924f, - 0.08030242f, 0.01220535f, 2.98233377e-003f, -0.01307070f, 0.05970297f, - -0.05345284f, -0.03381982f, -9.87543724e-003f, -0.06869387f, - 0.03956730f, -0.03108176f, -0.05732809f, 0.02172386f, 0.04159765f, - 2.62783933e-003f, 0.04813229f, 0.09358983f, -8.18389002e-003f, - 0.01724574f, -0.02547474f, -0.04967288f, -0.02390376f, 0.06640504f, - -0.06306566f, 0.01137518f, 0.05589378f, -0.08237787f, 0.02455001f, - -0.03059422f, -0.08953978f, 0.06851497f, 0.07190268f, -0.07610799f, - 7.87237938e-003f, -7.85830803e-003f, 0.06006952f, -0.01126728f, - -2.85743061e-003f, -0.04772895f, 0.01884944f, 0.15005857f, - -0.06268821f, -0.01989072f, 0.01138399f, 0.08760451f, 0.03879007f, - -9.66926850e-003f, -0.08012961f, 0.06414555f, -0.01362950f, - -0.09135523f, 0.01755159f, 0.04459474f, 0.09650917f, 0.05219948f, - -2.19440833e-003f, -0.07037939f, -0.01599054f, 0.13103317f, - -0.02492603f, -0.01032540f, -0.02903307f, 0.04489160f, 0.05148086f, - 0.01858173f, -0.02919228f, 0.08299296f, -0.04590359f, -0.15745632f, - -0.09068198f, -0.02972453f, 0.12985018f, 0.22320485f, 0.24261914f, - 0.03642650f, -0.05506422f, 2.67413049e-003f, -0.03834032f, 0.06449424f, - 0.03834866f, 0.03816991f, 0.25039271f, 0.34212017f, 0.32433882f, - 0.18824573f, -0.08599839f, -0.17599408f, -0.15317015f, -0.09913155f, - -0.02856072f, -0.05304699f, -1.06437842e-003f, -0.06641813f, - -0.07509298f, 0.01463361f, -0.07551918f, -0.04510373f, - -8.44620075e-003f, 0.01772176f, 0.04068235f, 0.20295307f, 0.15719447f, - 0.05712103f, 0.26296997f, 0.14657754f, 0.01547317f, -0.05052776f, - -0.03881342f, -0.01437883f, -0.04930177f, 0.11719568f, 0.24098417f, - 0.26468599f, 0.31698579f, 0.10103608f, -0.01096375f, -0.01367013f, - 0.17104232f, 0.20065314f, 2.67622480e-003f, -0.01190034f, 0.18301608f, - 0.09459770f, -0.06357619f, -0.06473801f, 0.01377906f, -0.10032775f, - -0.06388740f, 3.80393048e-003f, 0.06206078f, 0.10349120f, 0.26804337f, - 8.17918684e-003f, -0.02314351f, 9.34422202e-003f, 0.09198381f, - 0.03681326f, -8.77339672e-003f, -0.09662418f, -0.02715708f, - 0.13503517f, 0.08962728f, -6.57071499e-003f, -0.03201199f, 0.28510824f, - 0.32095715f, 0.18512695f, -0.14230858f, -0.14048551f, -0.07181299f, - -0.08575408f, -0.08661680f, -0.17416079f, 7.54326640e-004f, - 0.05601677f, 0.13585392f, -0.04960437f, -0.07708392f, 0.10676333f, - -0.04407546f, -0.07209078f, 0.03663663f, 0.28949317f, 0.41127121f, - 0.27431169f, -0.06900328f, -0.21474190f, -0.15578632f, -0.19555484f, - -0.15209621f, -0.11269179f, 0.07416003f, 0.18991330f, 0.26858172f, - 0.01952259f, 0.01017922f, 0.02159843f, -4.95165400e-003f, -0.04368168f, - -0.12721671f, -0.06673957f, -0.11275250f, 0.04413409f, 0.05578312f, - 0.03896771f, 0.03566417f, -0.05871816f, -0.07388090f, -0.17965563f, - -0.08570268f, -0.15273231f, -0.06022318f, -0.06999847f, - -6.81510568e-003f, 0.06294262f, -6.54901436e-004f, -0.01128654f, - -0.02289657f, 0.04849290f, 0.04140804f, 0.23681939f, 0.14545733f, - 0.01989965f, 0.12032662f, 3.87463090e-003f, -6.02597650e-003f, - -0.05919775f, -0.03067224f, -0.07787777f, 0.10834727f, 0.02153730f, - 0.02765649f, 0.03975543f, -0.12182906f, -0.04900113f, -0.09940100f, - -0.06453611f, -0.13757215f, -0.03721382f, 0.02827376f, -0.04351249f, - 0.01907038f, -0.10284120f, -0.05671160f, -0.10760647f, -0.09624009f, - -0.09565596f, -0.01303654f, 0.03080539f, 0.01416511f, 0.05846142f, - -5.42971538e-003f, 0.06221476f, -0.03320325f, -0.06791797f, - -0.05791342f, 0.12851369f, 0.14990346f, 0.03634374f, 0.14262885f, - 0.04330391f, 0.05032569f, -0.05631914f, 0.01606137f, 0.04387223f, - 0.22344995f, 0.15722635f, -0.04693628f, 0.03006579f, -2.52882647e-003f, - 0.05717621f, -0.07529724f, -0.02848588f, -0.06868757f, - -4.51729307e-003f, 0.06466042f, -0.05935378f, -0.04704857f, - -0.07363959f, 0.04843248f, -0.13421375f, -0.09789340f, -0.10255270f, - 0.03509852f, 0.04751543f, -0.03822323f, 0.09740467f, 0.04762916f, - 0.03940146f, -0.08283259f, 0.09552965f, 0.05038739f, 0.21258622f, - 0.09646992f, 0.03241193f, 0.05167701f, 0.04614570f, 0.04330090f, - -0.02671840f, -0.06259909f, -0.02301898f, 0.18829170f, 0.10522786f, - 0.04313190f, 0.01670948f, -0.08421925f, 0.05911417f, -0.10582602f, - -0.04855484f, -0.08373898f, 0.07775915f, 0.03723533f, -0.12047344f, - 4.86345543e-003f, -0.10520902f, 0.06571782f, -0.07528137f, - -0.03245651f, -0.09869066f, -0.02917477f, -0.18293270f, 0.14810945f, - 9.24033765e-003f, -0.04354914f, 0.02266885f, -0.11872729f, - -0.04016589f, 0.02830229f, 0.22539048f, 0.20565644f, 0.16701797f, - 0.09019924f, 0.01300652f, 0.09760600f, -0.03675831f, -0.01935448f, - -0.06894835f, 0.08077277f, 0.19047537f, 0.11312226f, 0.04106043f, - -0.11187182f, 0.04312806f, -0.18548580f, -0.11287174f, -0.08794551f, - 0.02078281f, -0.15295486f, 0.11806386f, -0.01103218f, -0.15971117f, - 0.02153538f, -0.05232147f, -0.10835317f, -0.13910367f, 0.05920752f, - -0.10122602f, 0.20174250f, 0.09105796f, -0.01881348f, 0.09559010f, - -0.03725745f, -0.09442931f, -0.09763174f, 0.05854454f, 0.08287182f, - 0.12919849f, 0.08594352f, -2.49806582e-003f, 0.02398440f, - 5.67950122e-003f, -0.06296340f, -0.12993270f, 0.03855852f, 0.05186560f, - 0.10839908f, -0.03380463f, -0.12654832f, -0.05399339f, -0.07456800f, - -0.04736232f, -0.10164231f, 0.07496139f, 0.08125214f, 0.07656177f, - -0.04999603f, -0.12823077f, -0.07692395f, -0.11317524f, -0.09118655f, - -0.05695669f, 0.10477209f, 0.07468581f, 0.01630048f, -8.00961629e-003f, - -0.06582128f, -0.04019095f, -0.04682907f, -0.01907842f, -0.10997720f, - 0.04911406f, 0.02931030f, 0.04197735f, -0.05773980f, -0.09670641f, - -0.03594951f, -0.03402121f, -0.07149299f, -0.10566200f, 0.10601286f, - 0.06340689f, -0.01518632f, -5.96402306e-003f, -0.07628012f, - -3.52779147e-003f, -0.02683854f, -0.10265494f, -0.02680815f, - 0.16338381f, 0.03103515f, 0.02296976f, 0.01624348f, -0.10831620f, - -0.02314233f, -0.04789969f, -0.05530700f, -0.06461314f, 0.10494506f, - 0.04642856f, -0.07592955f, -0.06197905f, -0.09042154f, -0.01445521f, - -0.04297818f, -0.11262015f, -0.11430512f, 0.03174541f, -0.03677487f, - -0.02963996f, -0.06610169f, -0.13292049f, -0.07059067f, -0.08444111f, - -0.02640536f, -0.07136250f, 0.04559967f, 0.01459980f, 0.17989251f, - 0.04435328f, -0.12464730f, -0.02871115f, -0.10752209f, -0.03393742f, - -0.03791408f, 0.02548251f, 0.01956050f, 0.19245651f, 0.13963254f, - -0.05904696f, -0.07424626f, -0.10411884f, 1.54176133e-003f, - 0.01797429f, 0.13025844f, 0.04547642f, -0.05710349f, -0.10697161f, - -0.13489437f, -0.06515755f, -0.06406886f, -4.08572936e-003f, - -0.01336483f, 0.04368737f, -0.11259720f, -0.05701635f, -0.06469971f, - -0.08346602f, -0.04166770f, -0.05795543f, -0.08247511f, -0.05742628f, - 0.08452254f, -0.03350224f, 0.13980860f, 0.13252275f, 0.07589617f, - 0.07539988f, 0.12155797f, 0.19087289f, 0.15050751f, 0.21250245f, - 0.14206800f, 0.01298489f, 0.07450245f, 0.06559097f, 0.01700557f, - 0.04512971f, 0.16950700f, 0.10261577f, 0.16389982f, 0.05505059f, - -0.03453077f, 0.08622462f, 0.07935954f, 0.03976260f, 0.02036091f, - 3.95744899e-003f, 0.03267065f, 0.15235919f, 0.01297494f, -0.08109194f, - 0.01407558f, 4.40693414e-003f, -0.15157418f, -0.11390478f, - -0.07487597f, -7.81322457e-003f, -0.02749545f, -0.10181408f, - 0.13755716f, 0.14007211f, 0.13482562f, 0.27517235f, 0.34251109f, - 0.07639657f, 0.07268607f, 0.19823882f, 0.16135791f, -0.04186463f, - -0.12784107f, -0.09846287f, 0.03169041f, 0.10974082f, -0.15051922f, - -0.08916726f, -0.07138767f, -0.04153349f, 6.25418453e-003f, - 0.01266654f, 0.10533249f, 0.12749144f, 0.15148053f, 0.01498513f, - 0.06305949f, -0.01247123f, -0.08778401f, -0.08551880f, -0.11955146f, - -0.08493572f, -0.02901620f, -0.02394859f, -0.13427313f, -0.11053200f, - -0.14413260f, -0.15203285f, 0.03972760f, -3.72127310e-004f, - -0.04200919f, 0.06105104f, 0.01904975f, -0.01106191f, - -7.27445772e-003f, -0.01520341f, 1.10228511e-003f, -0.04949187f, - -0.08013099f, 5.72071038e-003f, 0.08415454f, -0.06523152f, 0.03664081f, - -0.02673042f, -0.12066154f, -0.03702074f, 0.06006580f, 0.01628682f, - -6.17772620e-003f, 0.08192339f, -3.41629819e-003f, 0.02870512f, - 0.05807141f, 0.04959986f, 0.04618251f, -0.04901629f, -0.10579574f, - 0.02274442f, 0.12070961f, 2.23597488e-003f, 0.09831765f, -0.03019848f, - -0.11181970f, -0.04961075f, 0.02498928f, -0.03714991f, -0.01619653f, - 0.02643486f, -7.62964319e-003f, -0.02882290f, -0.06242594f, - -0.08439861f, 0.07220893f, 0.07263952f, 0.01561574f, 0.03091968f, - 0.01708712f, -0.03797151f, -3.18561122e-003f, 0.01624021f, - -0.02828573f, 0.11284444f, -1.32280716e-003f, -0.07784860f, - -0.07209100f, 0.03372242f, 0.12154529f, 0.02278104f, -0.05275500f, - -0.01918484f, 0.12989293f, 0.05424401f, 0.02333086f, 0.04029022f, - 0.12392918f, 0.09495489f, 0.09190340f, 0.07935889f, 8.76816828e-003f, - 0.17148446f, -8.51302687e-003f, -0.08011249f, -0.06796283f, - 0.04884845f, 0.01112272f, -0.07835306f, -1.14811445e-003f, - -0.03440760f, 0.02845243f, 0.07695542f, -0.07069533f, -0.01151784f, - -8.53884313e-003f, -0.01662786f, -0.04163864f, 0.05400505f, - 0.02859163f, 0.02921852f, 0.05003135f, -6.85718050e-003f, -0.01632611f, - 0.07780217f, 0.04042810f, -0.01216440f, 3.60914599e-003f, -0.06322435f, - 0.09516726f, 0.12877031f, -9.69162490e-003f, 0.01031179f, 0.05180895f, - -9.34659224e-003f, -0.01644533f, -0.04849347f, -0.04343236f, - 0.10514783f, 0.08046635f, -0.04615205f, -0.03975486f, -0.01485525f, - 0.13096830f, -0.01517950f, -0.06571898f, -0.04016372f, 0.01849786f, - 0.02439670f, 0.08067258f, 1.74824719e-003f, 0.07053747f, 0.08819518f, - -5.08352555e-003f, -0.06550863f, -0.08266170f, -0.07780605f, - 0.01453450f, -0.08756890f, 0.01096501f, -8.71319138e-003f, 0.10110464f, - 0.02420769f, -0.06708383f, 0.02007811f, 5.93133038e-003f, 0.05398923f, - 0.07538138f, 0.02049227f, 0.02242589f, 0.04011070f, -1.44875818e-003f, - -4.19115182e-003f, 0.06367654f, 0.02506934f, 0.02434536f, 0.05879405f, - -8.22952855e-003f, -0.01242441f, 0.04224926f, -0.01754923f, - 0.05958161f, 0.03818886f, -0.01830363f, -0.04308917f, -0.04422197f, - -0.02432721f, 0.02264866f, 2.03751423e-003f, 0.01197031f, 0.04439203f, - 0.12169247f, 0.03602713f, -0.02599251f, -1.98226492e-003f, 0.02046336f, - -0.02639058f, -1.91242550e-003f, -0.09334669f, -0.03595153f, - -9.88179818e-003f, -0.06848445f, -0.04666303f, -0.09955736f, - -0.04206430f, 0.02609075f, 9.09005292e-003f, -0.07138551f, - -4.22313227e-004f, 0.01766645f, 0.02756404f, 0.01308276f, 0.04052891f, - 0.02387515f, 0.05337298f, 0.02500631f, -0.04970853f, -0.12467445f, - 0.17604403f, 0.12256411f, -0.07512254f, 8.70451052e-003f, -0.05697548f, - -0.03626474f, -8.76623299e-003f, -0.01210897f, -0.09451522f, - 0.07490732f, -0.02008001f, -0.02681278f, -0.06463405f, -0.01517507f, - 7.33757764e-003f, 6.07147906e-003f, -0.09316964f, -0.04575328f, - 0.13261597f, 0.15424870f, -0.01655918f, -0.02772390f, -0.05243644f, - -0.02356456f, -0.02351753f, -0.10211615f, -0.12873036f, 0.14549787f, - 0.12519856f, 4.38762689e-003f, 0.02795992f, 0.05170322f, 0.09223596f, - 0.05890015f, 0.02376701f, -0.02777346f, 0.09506908f, 0.02328936f, - -0.02319928f, -0.03218696f, -0.01527841f, -0.01016694f, -0.02674719f, - 0.05137179f, 0.01980666f, 0.06544447f, -0.01746171f, 0.01026380f, - 0.01561806f, 7.97004555e-004f, 0.07601810f, 0.01907250f, -0.03083035f, - -0.05987392f, 0.09242783f, 0.14555025f, 0.01035827f, 0.03092401f, - -0.09562709f, -0.03802354f, 0.02531144f, 0.03079449f, -0.07100715f, - 0.03330721f, -2.69116857e-003f, 0.03167490f, 0.05744999f, 0.03259895f, - 1.91266940e-003f, 0.03194578f, 0.07389776f, 0.02198060f, 0.07633314f, - 0.03293105f, -0.09103648f, 0.04718142f, 0.06102672f, -0.01003063f, - 5.85481385e-003f, -0.01522574f, 0.02323526f, 0.10584345f, - 4.35879454e-003f, 0.06107873f, 0.05868603f, -0.03115531f, 0.01214679f, - 0.08567052f, 3.93926632e-003f, -0.02521488f, -1.88425183e-003f, - 0.02038053f, -6.26854831e-004f, 0.04897438f, -0.04280585f, - -0.04819689f, -0.04812867f, -0.01451186f, 0.05101469f, - -9.01125465e-003f, -0.03333859f, 0.03917955f, 0.04196448f, 0.04292135f, - 0.02809529f, 0.02999715f, 0.04081348f, 9.10039060e-003f, 0.09703232f, - 0.10379741f, 0.02348725f, -4.72756615e-003f, 0.01027325f, 0.10402658f, - 0.12071823f, 0.09817299f, -0.02612033f, 0.03638414f, 0.05896405f, - 0.04865025f, 0.04793910f, -0.03882321f, -0.02962117f, -0.01222268f, - 0.04071597f, 0.01922777f, -0.02287866f, 0.03328381f, 0.01859092f, - 0.09024994f, 0.03804455f, -0.01424510f, 0.01953739f, 0.02509617f, - -0.03390914f, -0.05663941f, -0.01641979f, 0.05848591f, 0.04639670f, - 0.02092116f, 0.12911791f, 0.19918139f, 0.07739855f, -7.25806039e-003f, - 0.04074838f, 0.03183993f, 1.39251316e-003f, -0.01428625f, 0.01865480f, - 0.08529541f, 0.13547510f, 0.11189661f, 0.03998901f, 0.09575938f, - -0.02631102f, -0.03458253f, -0.04749985f, -0.06070716f, - 4.71884012e-003f, 0.06445789f, -0.02450038f, -0.05483776f, - -0.04657237f, -0.02030717f, -0.03480766f, -0.09397731f, -0.06399718f, - -0.01804585f, 5.62348310e-003f, -6.64811488e-003f, -0.06517869f, - 6.96210237e-003f, -0.01860148f, -0.04245830f, -0.05850367f, - -3.24417115e-003f, 0.07700698f, 0.11290991f, 0.09923030f, -0.02970599f, - 0.05592411f, 0.04813979f, -0.09811195f, -0.09357996f, -0.03276114f, - 0.05218338f, 0.04141375f, 3.92977800e-003f, -0.05047480f, 0.15960084f, - 0.04612800f, -0.03114098f, -0.04650044f, -0.03249795f, -0.02425641f, - -0.04311355f, 0.04307659f, -0.09401883f, -0.04742785f, -0.01254499f, - -0.06598741f, 3.41369561e-003f, -0.05620445f, -7.28127593e-003f, - -0.05998361f, -0.03274450f, -0.07376868f, 3.19015374e-003f, - -0.07733069f, 0.05815864f, -0.02471071f, 0.03850617f, 0.13838784f, - 0.15399861f, 0.01731321f, -0.01477586f, 0.10393341f, 0.05159833f, - -0.01945555f, -0.03427503f, -0.04867341f, 0.09237480f, 0.10732719f, - 0.06071450f, -0.01355071f, 0.01844356f, -0.03480803f, -0.03796671f, - 2.15628621e-004f, -0.05440186f, 0.01889855f, -0.01443413f, - -0.02607902f, -0.02938001f, 0.02720689f, -0.06228397f, -0.02970936f, - -0.03426210f, -0.10280876f, -0.06739304f, -0.05227850f, 0.03360292f, - -0.11278441f, -0.06966180f, -0.13937433f, 9.10932291e-003f, - 2.52020749e-004f, -4.07359656e-003f, 0.12310639f, 0.09343060f, - 0.07302511f, 0.03222093f, 0.07532879f, 0.03792387f, -0.04985180f, - 0.01804602f, 0.02694195f, 0.13481498f, 0.04601225f, 0.04106982f, - 0.08511057f, 0.12314661f, 0.01320830f, 0.05044121f, -5.52943908e-003f, - -0.08992624f, -0.02249301f, -0.08181777f, 0.06165213f, -0.03256603f, - -0.01068920f, -0.01323473f, -0.11970232f, -0.04616347f, -0.12088681f, - -0.06762606f, -0.08676834f, -0.06434575f, 0.01772529f, 0.03469615f, - -0.10926618f, 0.03013873f, 0.14030397f, 0.16130108f, 0.17985588f, - 0.11281928f, 0.10530639f, 0.08905948f, 0.07733764f, 0.06695238f, - 0.02142088f, 0.06438877f, 0.09794453f, 0.05745072f, 0.02788557f, - 0.02632830f, 0.07985807f, 4.24902979e-003f, 8.47890321e-003f, - -0.02679466f, -5.28812688e-003f, -0.02162580f, -0.07490715f, - -0.08251337f, -0.02056576f, -0.01026194f, -1.15492963e-003f, - -5.75720915e-004f, -0.07210591f, -0.07320981f, -0.04883312f, - -0.10897151f, -0.07477258f, -0.08867134f, -0.09222437f, -0.10924666f, - -0.10430276f, 0.07953499f, 0.02767959f, 0.11393359f, 0.18779543f, - 0.03313421f, 0.02143700f, 0.05852016f, -2.12067598e-003f, - -3.76984011e-003f, 0.02774167f, -0.03124610f, 0.01465141f, 0.01616004f, - -0.01391913f, -0.04404102f, -0.05444227f, -0.14684731f, -0.15016587f, - 0.04509468f, 1.29563001e-003f, 0.01398350f, 0.05610404f, -0.04868806f, - -0.04776716f, -8.16873740e-003f, -2.30126386e-003f, -0.02286313f, - 0.11983398f, -0.04703261f, -0.08814441f, -0.07585249f, -0.10799607f, - -0.03232087f, 0.01509786f, -0.04843464f, -0.03967846f, 0.09589416f, - 0.01352560f, -0.01458119f, 0.01050829f, -0.03038946f, 0.01608388f, - 1.11975556e-003f, -0.01250656f, 2.86211423e-003f, 0.04333691f, - -0.14603497f, -0.01946543f, -0.02327525f, -0.01973944f, 0.07944400f, - -0.02224544f, -0.06701808f, 0.03476532f, 0.11505594f, -0.02712801f, - -0.01665113f, 0.06315716f, -0.08205860f, 0.07431999f, 0.04915778f, - -0.04468752f, -0.01490402f, 0.07400476f, -0.11650901f, 0.05102430f, - 0.04559118f, -0.05916039f, 0.08840760f, -0.01587902f, -0.14890194f, - 0.07857784f, 0.04710254f, -0.05381983f, -0.07331945f, -0.03604643f, - 0.15611970f, 0.07649943f, -0.05959348f, -0.02776607f, 0.11098688f, - 0.03758875f, -0.04446875f, 0.04933187f, 0.01345535f, 0.06921103f, - 0.07364785f, 0.05518956f, 0.02899585f, 0.09375840f, 0.10518434f, - -0.04420241f, 0.01915282f, -3.56386811e-003f, 0.14586878f, 0.10286101f, - -0.04360626f, -0.12723237f, 0.09076386f, 0.11119842f, -0.06035013f, - 0.09674817f, 0.08938243f, 0.07065924f, 0.02603180f, 5.84815582e-003f, - -0.05922065f, 0.12360309f, 3.59695964e-003f, 2.99844006e-003f, - 0.03697936f, 0.02043072f, 0.04168725f, 0.01025975f, -0.01359980f, - -0.01600920f, 0.02581056f, 0.02329250f, 2.98100687e-003f, 0.01629762f, - 0.06652115f, 0.05855627f, 0.01237463f, -0.01297135f, 0.01761587f, - 0.05090865f, 0.06549342f, -0.04425945f, 2.43203156e-003f, - 3.07327788e-003f, 0.06678630f, -0.04303836f, 0.01082393f, -0.06476044f, - 0.04077786f, 0.12441979f, 0.08237778f, 0.07424165f, 0.04065890f, - 0.06905543f, 0.09556347f, 0.12724875f, -0.02132082f, 0.08514154f, - -0.04175328f, -0.02666954f, 0.01897836f, 0.03317382f, 9.45465732e-003f, - -0.01238974f, -0.04242500f, -0.01419479f, -0.03545213f, -0.02440874f, - 0.08684119f, 0.04212951f, 0.02462858f, -0.01104825f, -5.01706870e-003f, - 0.02968982f, 0.02597476f, -0.01568939f, 0.04514892f, 0.06974549f, - 0.08670278f, 0.06828108f, 0.10238872f, 0.05405957f, 0.06548470f, - -0.03763957f, 0.01366090f, 0.07069602f, 0.05363748f, 0.04798120f, - 0.11706422f, 0.05466456f, -0.01869259f, 0.06344382f, 0.03106543f, - 0.08432506f, -0.02061096f, 0.03821088f, -6.92190882e-003f, - 6.40467042e-003f, -0.01271779f, 6.89014705e-005f, 0.04541415f, - -0.01899539f, -0.05020239f, 0.03000903f, 0.01090422f, 4.52452758e-003f, - 0.02573632f, -0.02388454f, -0.04200457f, 1.72783900e-003f, - -0.05978370f, -0.02720562f, 0.06573715f, 0.01154317f, 0.01265615f, - 0.07375994f, -9.19828378e-003f, -0.04914120f, 0.02124831f, 0.06455322f, - 0.04372910f, -0.03310043f, 0.03605788f, -6.78055827e-003f, - 9.36202332e-003f, 0.01747596f, -0.06406314f, -0.06812935f, 0.08080816f, - -0.02778088f, 0.02735260f, 0.06393493f, 0.06652229f, 0.05676993f, - 0.08640018f, -7.59188086e-003f, -0.02012847f, -0.04741159f, - -0.01657069f, -0.01624399f, 0.05547778f, -2.33309763e-003f, - 0.01120033f, 0.06141156f, -0.06285004f, -0.08732341f, -0.09313398f, - -0.04267832f, 5.57443965e-003f, 0.04809862f, 0.01773641f, - 5.37361018e-003f, 0.14842421f, -0.06298012f, -0.02935147f, 0.11443478f, - -0.05034208f, 5.65494271e-003f, 0.02076526f, -0.04577984f, - -0.04735741f, 0.02961071f, -0.09307127f, -0.04417921f, -0.04990027f, - -0.03940028f, 0.01306016f, 0.06267900f, 0.03758737f, 0.08460117f, - 0.13858789f, 0.04862388f, -0.06319809f, -0.05655516f, 0.01885816f, - -0.03285607f, 0.03371567f, -0.07040928f, -0.04514049f, 0.01392166f, - 0.08184422f, -0.07230316f, 0.02386871f, 0.02184591f, 0.02605764f, - -0.01033954f, 9.29878280e-003f, 7.67351175e-003f, 0.15189242f, - 0.02069071f, -0.09738296f, -0.08894105f, -0.07768748f, 0.02332268f, - -0.01778995f, -0.03258888f, -0.08180822f, -0.08492987f, 0.02290156f, - -0.11368170f, -0.03554465f, -0.04533844f, -0.02861580f, 0.06782424f, - 0.01113123f, 0.02453644f, 0.12721945f, 0.08084814f, -0.03607795f, - 0.01109122f, 0.04803548f, -0.03489929f, 0.03399536f, -0.05682014f, - 8.59533902e-003f, -4.27904585e-003f, 0.03230887f, -0.01300198f, - -0.01038137f, -0.07930113f, 8.33097473e-003f, 0.02296994f, - -0.01306500f, -0.01881626f, 0.04413369f, 0.05729880f, -0.03761553f, - 0.01942326f, 1.64540811e-003f, -0.03811319f, 0.04190650f, -0.14978096f, - -0.04514487f, 0.01209545f, -5.46460645e-003f, -0.01647195f, - 7.63064111e-003f, -0.07494587f, 0.08415288f, 0.10020141f, -0.01228561f, - 0.06553826f, 0.04554005f, 0.07890417f, 0.03041138f, 0.01752007f, - 0.09208256f, -3.74419295e-004f, 0.10549527f, 0.04686913f, 0.01894833f, - -0.02651412f, -4.34682379e-003f, 5.44942822e-003f, 0.01444484f, - 0.05882156f, -0.03336544f, 0.04603891f, -0.10432546f, 0.01923928f, - 0.01842845f, -0.01712168f, -0.02222766f, 0.04693324f, -0.06202956f, - -0.01422159f, 0.08732220f, -0.07706107f, 0.02661049f, -0.04300238f, - -0.03092422f, -0.03552184f, -0.01886088f, -0.04979934f, 0.03906401f, - 0.04608644f, 0.04966111f, 0.04275464f, -0.04621769f, -0.02653212f, - 8.57011229e-003f, 0.03839684f, 0.05818764f, 0.03880796f, - -2.76100676e-004f, 0.03076511f, -0.03266929f, -0.05374557f, - 0.04986527f, -9.45429131e-003f, 0.03582499f, -2.64564669e-003f, - -1.07461517e-003f, 0.02962313f, -0.01483363f, 0.03060869f, 0.02448327f, - 0.01845641f, 0.03282966f, -0.03534438f, -0.01084059f, -0.01119136f, - -1.85360224e-003f, -5.94652840e-004f, -0.04451817f, 2.98327743e-003f, - 0.06272484f, -0.02152076f, -3.05971340e-003f, -0.05070828f, - 0.01531762f, 0.01282815f, 0.05167150f, 9.46266949e-003f, - -3.34558333e-003f, 0.11442288f, -0.03906701f, -2.67325155e-003f, - 0.03069184f, -0.01134165f, 0.02949462f, 0.02879886f, 0.03855566f, - -0.03450781f, 0.09142872f, -0.02156654f, 0.06075062f, -0.06220816f, - 0.01944680f, 6.68372354e-003f, -0.06656796f, 8.70784000e-003f, - 0.03456013f, 0.02434320f, -0.13236357f, -0.04177035f, -0.02069627f, - 0.01068112f, 0.01505432f, -0.07517391f, -3.83571628e-003f, - -0.06298508f, -0.02881260f, -0.13101046f, -0.07221562f, - -5.79945277e-003f, -8.57300125e-003f, 0.03782469f, 0.02762164f, - 0.04942456f, -0.02936396f, 0.09597211f, 0.01921411f, 0.06101191f, - -0.04787507f, -0.01379578f, -7.40224449e-003f, -0.02220136f, - -0.01313756f, 7.77558051e-003f, 0.12296968f, 0.02939998f, 0.03594062f, - -0.07788624f, -0.01133144f, 3.99316690e-004f, -0.06090347f, - -0.01122066f, -4.68682544e-003f, 0.07633100f, -0.06748922f, - -0.05640298f, -0.05265681f, -0.01139122f, -0.01624347f, -0.04715714f, - -0.01099092f, 0.01048561f, 3.28499987e-003f, -0.05810167f, - -0.07699911f, -0.03330683f, 0.04185145f, 0.03478536f, 0.02275165f, - 0.02304766f, 6.66040834e-003f, 0.10968148f, -5.93013782e-003f, - -0.04858336f, -0.04203213f, -0.09316786f, -6.13074889e-003f, - -0.02544625f, 0.01366201f, 9.18555818e-003f, -0.01846578f, - -0.05622401f, -0.03989377f, -0.07810296f, 6.91275718e-003f, - 0.05957597f, -0.03901334f, 0.01572002f, -0.01193903f, - -6.89400872e-003f, -0.03093356f, -0.04136098f, -0.01562869f, - -0.04604580f, 0.02865234f, -0.08678447f, -0.03232484f, -0.05364593f, - -0.01445016f, -0.07003860f, -0.08669746f, -0.04520775f, 0.04274122f, - 0.03117515f, 0.08175703f, 0.01081109f, 0.06379741f, 0.06199206f, - 0.02865988f, 0.02360346f, 0.06725410f, -0.03248780f, -9.37702879e-003f, - 0.08265898f, -0.02245839f, 0.05125763f, -0.01862395f, 0.01973453f, - -0.01994494f, -0.10770868f, 0.03180375f, 3.23935156e-003f, - -0.02142080f, -0.04256190f, 0.04760900f, 0.04282863f, 0.05635953f, - -0.01870849f, 0.05540622f, -0.03042666f, 0.01455277f, -0.06630179f, - -0.05843807f, -0.03739681f, -0.09739155f, -0.03220233f, -0.05620182f, - -0.10381401f, 0.07400211f, 4.20676917e-003f, 0.03258535f, - 2.14308966e-003f, 0.05121966f, -0.01274337f, 0.02384761f, 0.06335578f, - -0.07905591f, 0.08375625f, -0.07898903f, -0.06508528f, -0.02498444f, - 0.06535810f, 0.03970535f, 0.04895468f, -0.01169566f, -0.03980601f, - 0.05682293f, 0.05925463f, -0.01165808f, -0.07936699f, -0.04208954f, - 0.01333987f, 0.09051196f, 0.10098671f, -0.03974256f, 0.01238771f, - -0.07501741f, -0.03655440f, -0.04301528f, 0.09216860f, - 4.63579083e-004f, 0.02851115f, 0.02142735f, 1.28244064e-004f, - 0.02879687f, -0.08554889f, -0.04838862f, 0.08135369f, -0.05756533f, - 0.01413900f, 0.03451880f, -0.06619488f, -0.03053130f, 0.02961676f, - -0.07384635f, 0.01135692f, 0.05283910f, -0.07778034f, -0.02107482f, - -0.05511716f, -0.13473752f, 0.03030157f, 0.06722020f, -0.06218817f, - -0.05826827f, 0.06254654f, 0.02895772f, -0.01664000f, -0.03620280f, - -0.01612278f, -1.46097376e-003f, 0.14013411f, -8.96181818e-003f, - -0.03250246f, 3.38630192e-003f, 2.64779478e-003f, 0.03359732f, - -0.02411991f, -0.04229729f, 0.10666174f, -6.66579151f }; - return std::vector(detector, detector + sizeof(detector)/sizeof(detector[0])); - } - -// This function renurn 1981 SVM coeffs obtained from daimler's base. -// To use these coeffs the detection window size should be (48,96) - std::vector HOGDescriptor::getDaimlerPeopleDetector() - { - static const float detector[] = { - 0.294350f, -0.098796f, -0.129522f, 0.078753f, - 0.387527f, 0.261529f, 0.145939f, 0.061520f, - 0.328699f, 0.227148f, -0.066467f, -0.086723f, - 0.047559f, 0.106714f, 0.037897f, 0.111461f, - -0.024406f, 0.304769f, 0.254676f, -0.069235f, - 0.082566f, 0.147260f, 0.326969f, 0.148888f, - 0.055270f, -0.087985f, 0.261720f, 0.143442f, - 0.026812f, 0.238212f, 0.194020f, 0.056341f, - -0.025854f, -0.034444f, -0.156631f, 0.205174f, - 0.089008f, -0.139811f, -0.100147f, -0.037830f, - -0.029230f, -0.055641f, 0.033248f, -0.016512f, - 0.155244f, 0.247315f, -0.124694f, -0.048414f, - -0.062219f, 0.193683f, 0.004574f, 0.055089f, - 0.093565f, 0.167712f, 0.167581f, 0.018895f, - 0.215258f, 0.122609f, 0.090520f, -0.067219f, - -0.049029f, -0.099615f, 0.241804f, -0.094893f, - -0.176248f, 0.001727f, -0.134473f, 0.104442f, - 0.050942f, 0.081165f, 0.072156f, 0.121646f, - 0.002656f, -0.297974f, -0.133587f, -0.060121f, - -0.092515f, -0.048974f, -0.084754f, -0.180111f, - -0.038590f, 0.086283f, -0.134636f, -0.107249f, - 0.132890f, 0.141556f, 0.249425f, 0.130273f, - -0.030031f, 0.073212f, -0.008155f, 0.019931f, - 0.071688f, 0.000300f, -0.019525f, -0.021725f, - -0.040993f, -0.086841f, 0.070124f, 0.240033f, - 0.265350f, 0.043208f, 0.166754f, 0.091453f, - 0.060916f, -0.036972f, -0.091043f, 0.079873f, - 0.219781f, 0.158102f, -0.140618f, -0.043016f, - 0.124802f, 0.093668f, 0.103208f, 0.094872f, - 0.080541f, 0.137711f, 0.160566f, -0.169231f, - 0.013983f, 0.309508f, -0.004217f, -0.057200f, - -0.064489f, 0.014066f, 0.361009f, 0.251328f, - -0.080983f, -0.044183f, 0.061436f, -0.037381f, - -0.078786f, 0.030993f, 0.066314f, 0.037683f, - 0.152325f, -0.091683f, 0.070203f, 0.217856f, - 0.036435f, -0.076462f, 0.006254f, -0.094431f, - 0.154829f, -0.023038f, -0.196961f, -0.024594f, - 0.178465f, -0.050139f, -0.045932f, -0.000965f, - 0.109112f, 0.046165f, -0.159373f, -0.008713f, - 0.041307f, 0.097129f, -0.057211f, -0.064599f, - 0.077165f, 0.176167f, 0.138322f, 0.065753f, - -0.104950f, 0.017933f, 0.136255f, -0.011598f, - 0.047007f, 0.080550f, 0.068619f, 0.084661f, - -0.035493f, -0.091314f, -0.041411f, 0.060971f, - -0.101912f, -0.079870f, -0.085977f, -0.022686f, - 0.079788f, -0.098064f, -0.054603f, 0.040383f, - 0.300794f, 0.128603f, 0.094844f, 0.047407f, - 0.101825f, 0.061832f, -0.162160f, -0.204553f, - -0.035165f, 0.101450f, -0.016641f, -0.027140f, - -0.134392f, -0.008743f, 0.102331f, 0.114853f, - 0.009644f, 0.062823f, 0.237339f, 0.167843f, - 0.053066f, -0.012592f, 0.043158f, 0.002305f, - 0.065001f, -0.038929f, -0.020356f, 0.152343f, - 0.043469f, -0.029967f, -0.042948f, 0.032481f, - 0.068488f, -0.110840f, -0.111083f, 0.111980f, - -0.002072f, -0.005562f, 0.082926f, 0.006635f, - -0.108153f, 0.024242f, -0.086464f, -0.189884f, - -0.017492f, 0.191456f, -0.007683f, -0.128769f, - -0.038017f, -0.132380f, 0.091926f, 0.079696f, - -0.106728f, -0.007656f, 0.172744f, 0.011576f, - 0.009883f, 0.083258f, -0.026516f, 0.145534f, - 0.153924f, -0.130290f, -0.108945f, 0.124490f, - -0.003186f, -0.100485f, 0.015024f, -0.060512f, - 0.026288f, -0.086713f, -0.169012f, 0.076517f, - 0.215778f, 0.043701f, -0.131642f, -0.012585f, - -0.045181f, -0.118183f, -0.241544f, -0.167293f, - -0.020107f, -0.019917f, -0.101827f, -0.107096f, - -0.010503f, 0.044938f, 0.189680f, 0.217119f, - -0.046086f, 0.044508f, 0.199716f, -0.036004f, - -0.148927f, 0.013355f, -0.078279f, 0.030451f, - 0.056301f, -0.024609f, 0.083224f, 0.099533f, - -0.039432f, -0.138880f, 0.005482f, -0.024120f, - -0.140468f, -0.066381f, -0.017057f, 0.009260f, - -0.058004f, -0.028486f, -0.061610f, 0.007483f, - -0.158309f, -0.150687f, -0.044595f, -0.105121f, - -0.045763f, -0.006618f, -0.024419f, -0.117713f, - -0.119366f, -0.175941f, -0.071542f, 0.119027f, - 0.111362f, 0.043080f, 0.034889f, 0.093003f, - 0.007842f, 0.057368f, -0.108834f, -0.079968f, - 0.230959f, 0.020205f, 0.011470f, 0.098877f, - 0.101310f, -0.030215f, -0.018018f, -0.059552f, - -0.106157f, 0.021866f, -0.036471f, 0.080051f, - 0.041165f, -0.082101f, 0.117726f, 0.030961f, - -0.054763f, -0.084102f, -0.185778f, -0.061305f, - -0.038089f, -0.110728f, -0.264010f, 0.076675f, - -0.077111f, -0.137644f, 0.036232f, 0.277995f, - 0.019116f, 0.107738f, 0.144003f, 0.080304f, - 0.215036f, 0.228897f, 0.072713f, 0.077773f, - 0.120168f, 0.075324f, 0.062730f, 0.122478f, - -0.049008f, 0.164912f, 0.162450f, 0.041246f, - 0.009891f, -0.097827f, -0.038700f, -0.023027f, - -0.120020f, 0.203364f, 0.248474f, 0.149810f, - -0.036276f, -0.082814f, -0.090343f, -0.027143f, - -0.075689f, -0.320310f, -0.000500f, -0.143334f, - -0.065077f, -0.186936f, 0.129372f, 0.116431f, - 0.181699f, 0.170436f, 0.418854f, 0.460045f, - 0.333719f, 0.230515f, 0.047822f, -0.044954f, - -0.068086f, 0.140179f, -0.044821f, 0.085550f, - 0.092483f, -0.107296f, -0.130670f, -0.206629f, - 0.114601f, -0.317869f, -0.076663f, 0.038680f, - 0.212753f, -0.016059f, -0.126526f, -0.163602f, - 0.210154f, 0.099887f, -0.126366f, 0.118453f, - 0.019309f, -0.021611f, -0.096499f, -0.111809f, - -0.200489f, 0.142854f, 0.228840f, -0.353346f, - -0.179151f, 0.116834f, 0.252389f, -0.031728f, - -0.188135f, -0.158998f, 0.386523f, 0.122315f, - 0.209944f, 0.394023f, 0.359030f, 0.260717f, - 0.170335f, 0.013683f, -0.142596f, -0.026138f, - -0.011878f, -0.150519f, 0.047159f, -0.107062f, - -0.147347f, -0.187689f, -0.186027f, -0.208048f, - 0.058468f, -0.073026f, -0.236556f, -0.079788f, - -0.146216f, -0.058563f, -0.101361f, -0.071294f, - -0.071093f, 0.116919f, 0.234304f, 0.306781f, - 0.321866f, 0.240000f, 0.073261f, -0.012173f, - 0.026479f, 0.050173f, 0.166127f, 0.228955f, - 0.061905f, 0.156460f, 0.205990f, 0.120672f, - 0.037350f, 0.167884f, 0.290099f, 0.420900f, - -0.012601f, 0.189839f, 0.306378f, 0.118383f, - -0.095598f, -0.072360f, -0.132496f, -0.224259f, - -0.126021f, 0.022714f, 0.284039f, 0.051369f, - -0.000927f, -0.058735f, -0.083354f, -0.141254f, - -0.187578f, -0.202669f, 0.048902f, 0.246597f, - 0.441863f, 0.342519f, 0.066979f, 0.215286f, - 0.188191f, -0.072240f, -0.208142f, -0.030196f, - 0.178141f, 0.136985f, -0.043374f, -0.181098f, - 0.091815f, 0.116177f, -0.126690f, -0.386625f, - 0.368165f, 0.269149f, -0.088042f, -0.028823f, - 0.092961f, 0.024099f, 0.046112f, 0.176756f, - 0.135849f, 0.124955f, 0.195467f, -0.037218f, - 0.167217f, 0.188938f, 0.053528f, -0.066561f, - 0.133721f, -0.070565f, 0.115898f, 0.152435f, - -0.116993f, -0.110592f, -0.179005f, 0.026668f, - 0.080530f, 0.075084f, -0.070401f, 0.012497f, - 0.021849f, -0.139764f, -0.022020f, -0.096301f, - -0.064954f, -0.127446f, -0.013806f, -0.108315f, - 0.156285f, 0.149867f, -0.011382f, 0.064532f, - 0.029168f, 0.027393f, 0.069716f, 0.153735f, - 0.038459f, 0.230714f, 0.253840f, 0.059522f, - -0.045053f, 0.014083f, 0.071103f, 0.068747f, - 0.095887f, 0.005832f, 0.144887f, 0.026357f, - -0.067359f, -0.044151f, -0.123283f, -0.019911f, - 0.005318f, 0.109208f, -0.003201f, -0.021734f, - 0.142025f, -0.066907f, -0.120070f, -0.188639f, - 0.012472f, -0.048704f, -0.012366f, -0.184828f, - 0.168591f, 0.267166f, 0.058208f, -0.044101f, - 0.033500f, 0.178558f, 0.104550f, 0.122418f, - 0.080177f, 0.173246f, 0.298537f, 0.064173f, - 0.053397f, 0.174341f, 0.230984f, 0.117025f, - 0.166242f, 0.227781f, 0.120623f, 0.176952f, - -0.011393f, -0.086483f, -0.008270f, 0.051700f, - -0.153369f, -0.058837f, -0.057639f, -0.060115f, - 0.026349f, -0.160745f, -0.037894f, -0.048575f, - 0.041052f, -0.022112f, 0.060365f, 0.051906f, - 0.162657f, 0.138519f, -0.050185f, -0.005938f, - 0.071301f, 0.127686f, 0.062342f, 0.144400f, - 0.072600f, 0.198436f, 0.246219f, -0.078185f, - -0.036169f, 0.075934f, 0.047328f, -0.013601f, - 0.087205f, 0.019900f, 0.022606f, -0.015365f, - -0.092506f, 0.075275f, -0.116375f, 0.050500f, - 0.045118f, 0.166567f, 0.072073f, 0.060371f, - 0.131747f, -0.169863f, -0.039352f, -0.047486f, - -0.039797f, -0.204312f, 0.021710f, 0.129443f, - -0.021173f, 0.173416f, -0.070794f, -0.063986f, - 0.069689f, -0.064099f, -0.123201f, -0.017372f, - -0.206870f, 0.065863f, 0.113226f, 0.024707f, - -0.071341f, -0.066964f, -0.098278f, -0.062927f, - 0.075840f, 0.014716f, 0.019378f, 0.132699f, - -0.074191f, -0.089557f, -0.078446f, -0.197488f, - -0.173665f, 0.052583f, 0.044361f, 0.113549f, - 0.098492f, 0.077379f, -0.011146f, -0.192593f, - -0.164435f, 0.045568f, 0.205699f, 0.049187f, - -0.082281f, 0.134874f, 0.185499f, 0.034968f, - -0.119561f, -0.112372f, -0.115091f, -0.054042f, - -0.183816f, -0.078100f, 0.190695f, 0.091617f, - 0.004257f, -0.041135f, -0.061453f, -0.141592f, - -0.194809f, -0.120638f, 0.020168f, 0.109672f, - 0.067398f, -0.015238f, -0.239145f, -0.264671f, - -0.185176f, 0.050472f, 0.020793f, 0.035678f, - 0.022839f, -0.052055f, -0.127968f, -0.113049f, - -0.228416f, -0.258281f, -0.053437f, 0.076424f, - 0.061450f, 0.237478f, 0.003618f, -0.055865f, - -0.108087f, -0.028937f, 0.045585f, 0.052829f, - -0.001471f, 0.022826f, 0.059565f, -0.104430f, - -0.077266f, -0.211882f, -0.212078f, 0.028074f, - 0.075846f, 0.016265f, 0.161879f, 0.134477f, - 0.008935f, -0.048041f, 0.074692f, 0.004928f, - -0.025156f, 0.192874f, 0.074410f, 0.308732f, - 0.267400f, 0.094208f, -0.005251f, 0.042041f, - -0.032148f, 0.015588f, 0.252869f, 0.175302f, - 0.022892f, 0.081673f, 0.063208f, 0.162626f, - 0.194426f, 0.233890f, 0.262292f, 0.186930f, - 0.084079f, -0.286388f, -0.213034f, -0.048867f, - -0.207669f, -0.170050f, 0.011673f, -0.092958f, - -0.192786f, -0.273536f, 0.230904f, 0.266732f, - 0.320519f, 0.297155f, 0.548169f, 0.304922f, - 0.132687f, 0.247333f, 0.212488f, -0.271472f, - -0.142105f, -0.002627f, -0.119215f, 0.128383f, - 0.100079f, -0.057490f, -0.121902f, -0.228892f, - 0.202292f, -0.399795f, -0.371326f, -0.095836f, - -0.063626f, -0.161375f, -0.311180f, -0.294797f, - 0.242122f, 0.011788f, 0.095573f, 0.322523f, - 0.511840f, 0.322880f, 0.313259f, 0.173331f, - 0.002542f, -0.029802f, 0.324766f, -0.326170f, - -0.340547f, -0.138288f, -0.002963f, -0.114060f, - -0.377312f, -0.442570f, 0.212446f, -0.007759f, - -0.011576f, 0.169711f, 0.308689f, 0.317348f, - 0.539390f, 0.332845f, 0.057331f, -0.068180f, - 0.101994f, 0.266995f, 0.209570f, 0.355730f, - 0.091635f, 0.170238f, 0.125215f, 0.274154f, - 0.070223f, 0.025515f, 0.049946f, -0.000550f, - 0.043715f, -0.141843f, 0.020844f, 0.129871f, - 0.256588f, 0.105015f, 0.148339f, 0.170682f, - 0.028792f, 0.074037f, 0.160042f, 0.405137f, - 0.246187f, 0.352160f, 0.168951f, 0.222263f, - 0.264439f, 0.065945f, 0.021963f, -0.075084f, - 0.093105f, 0.027318f, 0.098864f, 0.057566f, - -0.080282f, 0.185032f, 0.314419f, 0.333727f, - 0.125798f, 0.294919f, 0.386002f, 0.217619f, - -0.183517f, -0.278622f, -0.002342f, -0.027821f, - -0.134266f, -0.331843f, -0.008296f, 0.124564f, - 0.053712f, -0.369016f, -0.095036f, 0.209381f, - 0.423760f, 0.371760f, 0.106397f, 0.369408f, - 0.485608f, 0.231201f, -0.138685f, -0.349208f, - -0.070083f, 0.028991f, -0.081630f, -0.395992f, - -0.146791f, -0.027354f, 0.063396f, -0.272484f, - 0.058299f, 0.338207f, 0.110767f, -0.052642f, - -0.233848f, -0.027448f, 0.030328f, 0.155572f, - -0.093826f, 0.019331f, 0.120638f, 0.006292f, - -0.106083f, -0.236290f, -0.140933f, -0.088067f, - -0.025138f, -0.208395f, -0.025502f, 0.144192f, - -0.048353f, -0.106144f, -0.305121f, -0.114147f, - 0.090963f, 0.327727f, 0.035606f, -0.093779f, - 0.002651f, -0.171081f, -0.188131f, -0.216571f, - -0.209101f, -0.054402f, 0.157147f, -0.057127f, - 0.066584f, 0.008988f, 0.041191f, 0.034456f, - -0.078255f, 0.052099f, -0.022239f, 0.066981f, - -0.117520f, -0.072637f, 0.062512f, 0.037570f, - -0.057544f, -0.312359f, 0.034357f, -0.031549f, - 0.002566f, -0.207375f, -0.070654f, -0.018786f, - -0.044815f, -0.012814f, -0.076320f, 0.078183f, - 0.023877f, 0.117078f, 0.022292f, -0.205424f, - -0.060430f, -0.017296f, -0.004827f, -0.321036f, - -0.092155f, 0.038837f, 0.073190f, -0.067513f, - 0.026521f, 0.171945f, 0.087318f, 0.034495f, - -0.034089f, 0.154410f, -0.061431f, 0.007435f, - -0.111094f, -0.095976f, 0.014741f, -0.132324f, - -0.029517f, -0.192160f, 0.098667f, 0.020762f, - 0.177050f, -0.064510f, -0.054437f, -0.058678f, - -0.001858f, 0.167602f, 0.015735f, 0.054338f, - 0.016477f, 0.186381f, -0.010667f, 0.054692f, - 0.126742f, 0.013140f, 0.090353f, -0.133608f, - -0.018017f, -0.152619f, 0.027600f, -0.138700f, - -0.050274f, 0.045141f, -0.118731f, 0.094797f, - -0.167605f, 0.097461f, -0.009131f, 0.199920f, - -0.052976f, 0.158194f, 0.178568f, -0.107600f, - 0.009671f, -0.084072f, -0.040258f, -0.205673f, - 0.102891f, 0.223511f, 0.042699f, 0.118548f, - -0.021274f, 0.110997f, -0.155121f, 0.027696f, - -0.149968f, 0.051552f, -0.129219f, 0.173524f, - 0.073972f, -0.189045f, -0.034523f, -0.106655f, - -0.011843f, -0.197381f, 0.219413f, 0.183197f, - -0.054920f, 0.144955f, 0.036517f, -0.085412f, - -0.229070f, -0.143710f, -0.049486f, 0.156634f, - -0.008673f, -0.064778f, 0.082344f, 0.145673f, - 0.002912f, -0.210121f, -0.116564f, 0.078425f, - 0.220908f, -0.067594f, 0.048610f, 0.084912f, - -0.066202f, -0.112515f, -0.217767f, -0.082640f, - -0.017414f, 0.230265f, -0.070735f, 0.066073f, - 0.215256f, 0.071157f, -0.087220f, -0.202235f, - -0.011918f, 0.099562f, 0.174716f, -0.063845f, - -0.121055f, 0.014367f, 0.132709f, -0.005060f, - -0.244606f, -0.179693f, -0.134690f, 0.023239f, - -0.193116f, -0.076975f, -0.021164f, -0.001938f, - -0.163799f, -0.111437f, -0.210362f, -0.166376f, - 0.034754f, 0.010036f, -0.021917f, 0.068014f, - -0.086893f, -0.251746f, -0.267171f, 0.037383f, - 0.003966f, 0.033571f, -0.151506f, 0.025437f, - -0.020626f, -0.308454f, -0.343143f, -0.092263f, - -0.026261f, -0.028345f, 0.036036f, 0.035169f, - 0.129470f, 0.122205f, 0.015661f, -0.070612f, - -0.094333f, -0.066055f, -0.041083f, 0.159146f, - 0.073184f, 0.110044f, 0.174471f, 0.078069f, - -0.014881f, 0.008116f, 0.013209f, 0.075857f, - 0.195605f, 0.062714f, 0.067955f, 0.056544f, - -0.153908f, -0.141749f, -0.072550f, 0.033523f, - -0.024665f, 0.134487f, 0.079076f, 0.133562f, - 0.227130f, 0.018054f, 0.004928f, 0.169162f, - 0.065152f, 0.072160f, 0.131631f, 0.096303f, - 0.054288f, 0.106256f, 0.114632f, 0.119038f, - 0.515200f, 0.247429f, 0.199134f, 0.211957f, - 0.127558f, -0.294684f, -0.194890f, -0.049988f, - -0.112247f, -0.008122f, -0.006176f, 0.037035f, - -0.110881f, -0.249989f, 0.152434f, 0.234621f, - 0.153340f, 0.349283f, 0.683049f, 0.157174f, - 0.124844f, 0.099136f, 0.064407f, -0.248400f, - -0.155323f, -0.026498f, -0.023450f, 0.049051f, - -0.114187f, 0.007195f, -0.176825f, -0.376926f, - 0.366159f, -0.179938f, -0.148508f, 0.006043f, - 0.170048f, 0.097866f, -0.102658f, -0.260430f, - 0.248868f, 0.037019f, -0.118111f, 0.078176f, - 0.194171f, 0.211328f, 0.368612f, 0.361213f, - 0.130013f, 0.094650f, 0.227396f, -0.178058f, - -0.114782f, -0.008093f, 0.231080f, -0.011843f, - -0.097917f, -0.325788f, 0.141879f, 0.119738f, - -0.230427f, -0.117419f, -0.114153f, 0.037903f, - 0.116383f, 0.218773f, -0.101884f, 0.059466f, - 0.119255f, 0.010874f, -0.031449f, 0.045996f, - 0.119931f, 0.273760f, 0.311700f, 0.261794f, - 0.194809f, 0.339829f, 0.239449f, 0.064140f, - 0.077597f, 0.098996f, 0.143534f, 0.184602f, - 0.037507f, 0.225494f, 0.096142f, -0.147370f, - -0.207833f, -0.174742f, -0.086391f, -0.038942f, - 0.159577f, -0.088492f, -0.000989f, 0.108154f, - -0.025890f, -0.072713f, 0.025997f, -0.006803f, - -0.086879f, -0.011290f, -0.269200f, -0.103450f, - -0.124910f, -0.116340f, 0.141459f, 0.208800f, - 0.042268f, 0.265034f, 0.516474f, 0.217591f, - -0.018843f, -0.313328f, -0.168363f, 0.047129f, - 0.090480f, -0.109852f, -0.018761f, 0.210669f, - 0.281269f, -0.043591f, -0.034147f, -0.237772f, - -0.134843f, -0.072481f, -0.103831f, 0.038355f, - 0.308619f, 0.148023f, -0.045867f, -0.123950f, - -0.210860f, -0.064973f, -0.036308f, -0.046731f, - -0.022099f, 0.095776f, 0.409423f, 0.060635f, - -0.065196f, 0.051828f, 0.027981f, -0.009609f, - -0.137681f, -0.095011f, -0.019045f, 0.177278f, - 0.009759f, -0.092119f, -0.016958f, -0.133860f, - -0.118421f, -0.032039f, -0.006214f, -0.084541f, - 0.063971f, -0.073642f, 0.165676f, 0.110443f, - 0.044131f, 0.046568f, 0.053292f, -0.055466f, - 0.015512f, 0.371947f, 0.232102f, -0.016923f, - 0.103979f, -0.091758f, 0.005907f, 0.209100f, - 0.157433f, 0.030518f, 0.250366f, 0.062322f, - 0.036720f, 0.094676f, 0.017306f, -0.010328f, - -0.079012f, 0.016781f, -0.112435f, 0.061795f, - 0.042543f, -0.126799f, -0.009975f, -0.056760f, - 0.046424f, -0.194712f, -0.139399f, -0.037731f, - 0.157989f, -0.016261f, 0.123345f, 0.230563f, - 0.083300f, -0.016392f, 0.059567f, -0.016035f, - -0.064767f, 0.231945f, 0.156629f, 0.034602f, - 0.145628f, 0.041315f, 0.034535f, 0.019967f, - -0.089188f, -0.012091f, 0.307857f, 0.211405f, - -0.025091f, -0.148249f, -0.129384f, 0.063536f, - -0.068603f, -0.067941f, -0.035104f, 0.210832f, - 0.063810f, 0.062764f, -0.089889f, -0.030554f, - 0.014791f, -0.053362f, -0.037818f, -0.196640f, - 0.008388f, -0.082654f, 0.143056f, 0.064221f, - 0.069795f, 0.191040f, 0.097321f, -0.028679f, - 0.075794f, 0.313154f, 0.086240f, 0.207643f, - 0.017809f, 0.122867f, 0.224586f, 0.167403f, - -0.023884f, 0.047434f, 0.344091f, 0.187745f, - 0.136177f, 0.141738f, 0.063799f, 0.045233f, - -0.077342f, -0.003525f, -0.165041f, -0.025616f, - -0.073745f, 0.164439f, 0.011200f, -0.145896f, - -0.027954f, -0.061987f, -0.039874f, -0.142775f, - 0.151042f, -0.038238f, 0.053152f, 0.078615f, - 0.086061f, 0.100593f, 0.128046f, -0.071006f, - -0.116558f, 0.208445f, 0.051086f, 0.076843f, - 0.023191f, -0.084781f, -0.011790f, 0.147807f, - -0.048554f, -0.113932f, 0.283322f, 0.190934f, - 0.092789f, 0.033018f, -0.142428f, -0.142480f, - -0.099023f, -0.041020f, -0.042760f, 0.203295f, - -0.053475f, 0.042424f, 0.222839f, -0.019167f, - -0.133176f, -0.276216f, -0.031998f, 0.117290f, - 0.177827f, -0.059973f, -0.064744f, -0.117040f, - -0.155482f, -0.099531f, 0.164121f, -0.026682f, - -0.093810f, 0.238993f, -0.006506f, 0.007830f, - 0.065819f, -0.203643f, -0.100925f, -0.053652f, - -0.130770f, 0.026277f, 0.131796f, 0.032742f, - 0.127186f, 0.116694f, -0.161122f, -0.279773f, - -0.252515f, -0.002638f, 0.042812f, 0.096776f, - -0.123280f, 0.064858f, -0.010455f, -0.219760f, - -0.239331f, -0.104363f, -0.058022f, -0.053584f, - 0.025611f, 0.005129f, -0.100418f, -0.045712f, - -0.194418f, -0.126366f, -0.030530f, 0.051168f, - 0.215959f, 0.172402f, -0.054700f, -0.185995f, - -0.278360f, -0.193693f, -0.040309f, 0.003735f, - -0.007770f, 0.123556f, 0.190179f, -0.077315f, - 0.117403f, 0.212942f, 0.012160f, 0.000113f, - 0.027331f, 0.040202f, 0.033293f, 0.219438f, - 0.184174f, 0.259349f, 0.311206f, 0.082547f, - -0.047875f, -0.078417f, 0.010746f, 0.082620f, - 0.311931f, 0.307605f, 0.003863f, 0.021405f, - -0.026388f, -0.019572f, 0.020582f, -0.059353f, - 0.025199f, 0.261319f, 0.086316f, 0.143614f, - 0.107780f, 0.003900f, -0.188397f, -0.038563f, - -0.106045f, -0.125154f, -0.010509f, 0.054021f, - 0.242130f, 0.279152f, 0.215546f, 0.346995f, - 0.440856f, 0.237452f, 0.234154f, 0.301646f, - 0.168929f, -0.208358f, -0.126848f, 0.010260f, - 0.121018f, -0.062975f, -0.052848f, 0.050341f, - -0.061103f, -0.266482f, 0.107186f, 0.140221f, - 0.280065f, 0.287889f, 0.373198f, 0.151596f, - 0.013593f, 0.115616f, 0.014616f, -0.281710f, - -0.237597f, -0.117305f, -0.000034f, -0.136739f, - -0.196275f, -0.095225f, -0.125310f, -0.250514f, - 0.236804f, -0.071805f, -0.037421f, 0.048230f, - 0.321596f, 0.063632f, 0.024039f, -0.029133f, - 0.230983f, 0.160593f, -0.154355f, -0.013086f, - -0.079929f, 0.094692f, 0.160391f, 0.180239f, - 0.053895f, 0.100759f, 0.288631f, 0.038191f, - 0.181692f, 0.229682f, 0.440166f, 0.063401f, - 0.006273f, 0.020865f, 0.338695f, 0.256244f, - -0.043927f, 0.115617f, 0.003296f, 0.173965f, - 0.021318f, -0.040936f, -0.118932f, 0.182380f, - 0.235922f, -0.053233f, -0.015053f, -0.101057f, - 0.095341f, 0.051111f, 0.161831f, 0.032614f, - 0.159496f, 0.072375f, 0.025089f, 0.023748f, - 0.029151f, 0.161284f, -0.117717f, -0.036191f, - -0.176822f, -0.162006f, 0.226542f, -0.078329f, - 0.043079f, -0.119172f, 0.054614f, -0.101365f, - -0.064541f, -0.115304f, 0.135170f, 0.298872f, - 0.098060f, 0.089428f, -0.007497f, 0.110391f, - -0.028824f, 0.020835f, -0.036804f, 0.125411f, - 0.192105f, -0.048931f, 0.003086f, -0.010681f, - 0.074698f, -0.016263f, 0.096063f, 0.060267f, - -0.007277f, 0.139139f, -0.080635f, 0.036628f, - 0.086058f, 0.131979f, 0.085707f, 0.025301f, - 0.226094f, 0.194759f, 0.042193f, -0.157846f, - -0.068402f, -0.141450f, -0.112659f, -0.076305f, - -0.069085f, -0.114332f, -0.102005f, 0.132193f, - -0.067042f, 0.106643f, 0.198964f, 0.171616f, - 0.167237f, -0.033730f, -0.026755f, 0.083621f, - 0.149459f, -0.002799f, -0.000318f, 0.011753f, - 0.065889f, -0.089375f, -0.049610f, 0.224579f, - 0.216548f, -0.034908f, -0.017851f, -0.088144f, - 0.007530f, 0.240268f, 0.073270f, 0.013263f, - 0.175323f, 0.012082f, 0.093993f, 0.015282f, - 0.105854f, 0.107990f, 0.077798f, -0.096166f, - -0.079607f, 0.177820f, 0.142392f, 0.033337f, - -0.078100f, -0.081616f, -0.046993f, 0.139459f, - 0.020272f, -0.123161f, 0.175269f, 0.105217f, - 0.057328f, 0.080909f, -0.012612f, -0.097081f, - 0.082060f, -0.096716f, -0.063921f, 0.201884f, - 0.128166f, -0.035051f, -0.032227f, -0.068139f, - -0.115915f, 0.095080f, -0.086007f, -0.067543f, - 0.030776f, 0.032712f, 0.088937f, 0.054336f, - -0.039329f, -0.114022f, 0.171672f, -0.112321f, - -0.217646f, 0.065186f, 0.060223f, 0.192174f, - 0.055580f, -0.131107f, -0.144338f, 0.056730f, - -0.034707f, -0.081616f, -0.135298f, -0.000614f, - 0.087189f, 0.014614f, 0.067709f, 0.107689f, - 0.225780f, 0.084361f, -0.008544f, 0.051649f, - -0.048369f, -0.037739f, -0.060710f, 0.002654f, - 0.016935f, 0.085563f, -0.015961f, -0.019265f, - 0.111788f, 0.062376f, 0.202019f, 0.047713f, - 0.042261f, 0.069716f, 0.242913f, 0.021052f, - -0.072812f, -0.155920f, -0.026436f, 0.035621f, - -0.079300f, -0.028787f, -0.048329f, 0.084718f, - -0.060565f, -0.083750f, -0.164075f, -0.040742f, - -0.086219f, 0.015271f, -0.005204f, -0.016038f, - 0.045816f, -0.050433f, -0.077652f, 0.117109f, - 0.009611f, -0.009045f, -0.008634f, -0.055373f, - -0.085968f, 0.028527f, -0.054736f, -0.168089f, - 0.175839f, 0.071205f, -0.023603f, 0.037907f, - -0.004561f, -0.022634f, 0.123831f, 0.094469f, - -0.072920f, -0.133642f, -0.014032f, -0.142754f, - -0.026999f, -0.199409f, 0.013268f, 0.226989f, - 0.048650f, -0.170988f, -0.050141f, 0.007880f, - 0.061880f, 0.019078f, -0.043578f, -0.038139f, - 0.134814f, 0.054097f, -0.081670f, 0.176838f, - 0.047920f, -0.038176f, 0.050406f, -0.107181f, - -0.036279f, 0.027060f, 0.081594f, -0.002820f, - 0.090507f, -0.033338f, -0.059571f, 0.013404f, - -0.099860f, 0.073371f, 0.342805f, 0.098305f, - -0.150910f, -0.020822f, -0.056960f, 0.046262f, - -0.043413f, -0.149405f, -0.129105f, -0.010899f, - -0.014229f, -0.179949f, -0.113044f, -0.049468f, - -0.065513f, 0.090269f, -0.011919f, 0.087846f, - 0.095796f, 0.146127f, 0.101599f, 0.078066f, - -0.084348f, -0.100002f, -0.020134f, -0.050169f, - 0.062122f, 0.014640f, 0.019143f, 0.036543f, - 0.180924f, -0.013976f, -0.066768f, -0.001090f, - -0.070419f, -0.004839f, -0.001504f, 0.034483f, - -0.044954f, -0.050336f, -0.088638f, -0.174782f, - -0.116082f, -0.205507f, 0.015587f, -0.042839f, - -0.096879f, -0.144097f, -0.050268f, -0.196796f, - 0.109639f, 0.271411f, 0.173732f, 0.108070f, - 0.156437f, 0.124255f, 0.097242f, 0.238693f, - 0.083941f, 0.109105f, 0.223940f, 0.267188f, - 0.027385f, 0.025819f, 0.125070f, 0.093738f, - 0.040353f, 0.038645f, -0.012730f, 0.144063f, - 0.052931f, -0.009138f, 0.084193f, 0.160272f, - -0.041366f, 0.011951f, -0.121446f, -0.106713f, - -0.047566f, 0.047984f, -0.255224f, -0.076116f, - 0.098685f, -0.150845f, -0.171513f, -0.156590f, - 0.058331f, 0.187493f, 0.413018f, 0.554265f, - 0.372242f, 0.237943f, 0.124571f, 0.110829f, - 0.010322f, -0.174477f, -0.067627f, -0.001979f, - 0.142913f, 0.040597f, 0.019907f, 0.025963f, - -0.043585f, -0.120732f, 0.099937f, 0.091059f, - 0.247307f, 0.204226f, -0.042753f, -0.068580f, - -0.119002f, 0.026722f, 0.034853f, -0.060934f, - -0.025054f, -0.093026f, -0.035372f, -0.233209f, - -0.049869f, -0.039151f, -0.022279f, -0.065380f, - -9.063785f}; - return std::vector(detector, detector + sizeof(detector)/sizeof(detector[0])); - } - - class HOGConfInvoker : - public ParallelLoopBody - { - public: - HOGConfInvoker( const HOGDescriptor* _hog, const Mat& _img, - double _hitThreshold, const Size& _padding, - std::vector* locs, - std::vector* _vec, Mutex* _mtx ) - { - hog = _hog; - img = _img; - hitThreshold = _hitThreshold; - padding = _padding; - locations = locs; - vec = _vec; - mtx = _mtx; - } - - void operator()(const Range& range) const CV_OVERRIDE - { - CV_INSTRUMENT_REGION(); - - int i, i1 = range.start, i2 = range.end; - - Size maxSz(cvCeil(img.cols/(*locations)[0].scale), cvCeil(img.rows/(*locations)[0].scale)); - Mat smallerImgBuf(maxSz, img.type()); - std::vector dets; - - for (i = i1; i < i2; i++) - { - double scale = (*locations)[i].scale; - - Size sz(cvRound(img.cols / scale), cvRound(img.rows / scale)); - Mat smallerImg(sz, img.type(), smallerImgBuf.ptr()); - - if (sz == img.size()) - smallerImg = Mat(sz, img.type(), img.data, img.step); - else - resize(img, smallerImg, sz, 0, 0, INTER_LINEAR_EXACT); - - hog->detectROI(smallerImg, (*locations)[i].locations, dets, (*locations)[i].confidences, hitThreshold, Size(), padding); - Size scaledWinSize = Size(cvRound(hog->winSize.width*scale), cvRound(hog->winSize.height*scale)); - mtx->lock(); - for (size_t j = 0; j < dets.size(); j++) - vec->push_back(Rect(cvRound(dets[j].x*scale), - cvRound(dets[j].y*scale), - scaledWinSize.width, scaledWinSize.height)); - mtx->unlock(); - } - } - - const HOGDescriptor* hog; - Mat img; - double hitThreshold; - std::vector* locations; - Size padding; - std::vector* vec; - Mutex* mtx; - }; - - void HOGDescriptor::detectROI(const cv::Mat& img, const std::vector &locations, - CV_OUT std::vector& foundLocations, CV_OUT std::vector& confidences, - double hitThreshold, cv::Size winStride, cv::Size padding) const - { - CV_INSTRUMENT_REGION(); - - foundLocations.clear(); - confidences.clear(); - - if (svmDetector.empty() || locations.empty()) - return; - - if (winStride == Size()) - winStride = cellSize; - Size cacheStride(gcd(winStride.width, blockStride.width), - gcd(winStride.height, blockStride.height)); - - size_t nwindows = locations.size(); - padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); - padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height); - Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2); - - // HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); - HOGCache cache(this, img, padding, padding, true, cacheStride); - if (!nwindows) - nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); - - const HOGCache::BlockData* blockData = &cache.blockData[0]; - - int nblocks = cache.nblocks.area(); - int blockHistogramSize = cache.blockHistogramSize; - size_t dsize = getDescriptorSize(); - - double rho = svmDetector.size() > dsize ? svmDetector[dsize] : 0; - std::vector blockHist(blockHistogramSize); - -#if CV_SIMD128 - float partSum[4]; -#endif - - for (size_t i = 0; i < nwindows; i++) - { - Point pt0; - pt0 = locations[i]; - if (pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width || - pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height) - { - // out of image - confidences.push_back(-10.0); - continue; - } - - double s = rho; - const float* svmVec = &svmDetector[0]; - int j, k; - - for (j = 0; j < nblocks; j++, svmVec += blockHistogramSize) - { - const HOGCache::BlockData& bj = blockData[j]; - Point pt = pt0 + bj.imgOffset; - - // need to divide this into 4 parts! - const float* vec = cache.getBlock(pt, &blockHist[0]); -#if CV_SIMD128 - v_float32x4 _vec = v_load(vec); - v_float32x4 _svmVec = v_load(svmVec); - v_float32x4 sum = _svmVec * _vec; - - for (k = 4; k <= blockHistogramSize - 4; k += 4) - { - _vec = v_load(vec + k); - _svmVec = v_load(svmVec + k); - - sum += _vec * _svmVec; - } - - v_store(partSum, sum); - - double t0 = partSum[0] + partSum[1]; - double t1 = partSum[2] + partSum[3]; - s += t0 + t1; -#else - for (k = 0; k <= blockHistogramSize - 4; k += 4) - s += vec[k]*svmVec[k] + vec[k+1]*svmVec[k+1] + - vec[k+2]*svmVec[k+2] + vec[k+3]*svmVec[k+3]; -#endif - for ( ; k < blockHistogramSize; k++) - s += vec[k]*svmVec[k]; - } - confidences.push_back(s); - - if (s >= hitThreshold) - foundLocations.push_back(pt0); - } - } - - void HOGDescriptor::detectMultiScaleROI(const cv::Mat& img, - CV_OUT std::vector& foundLocations, std::vector& locations, - double hitThreshold, int groupThreshold) const - { - CV_INSTRUMENT_REGION(); - - std::vector allCandidates; - Mutex mtx; - - parallel_for_(Range(0, (int)locations.size()), - HOGConfInvoker(this, img, hitThreshold, Size(8, 8), - &locations, &allCandidates, &mtx)); - - foundLocations.resize(allCandidates.size()); - std::copy(allCandidates.begin(), allCandidates.end(), foundLocations.begin()); - cv::groupRectangles(foundLocations, groupThreshold, 0.2); - } - - void HOGDescriptor::readALTModel(String modelfile) - { - // read model from SVMlight format.. - FILE *modelfl; - if ((modelfl = fopen(modelfile.c_str(), "rb")) == NULL) - { - String eerr("file not exist"); - String efile(__FILE__); - String efunc(__FUNCTION__); - throw Exception(Error::StsError, eerr, efile, efunc, __LINE__); - } - char version_buffer[10]; - if (!fread (&version_buffer,sizeof(char),10,modelfl)) - { - String eerr("version?"); - String efile(__FILE__); - String efunc(__FUNCTION__); - fclose(modelfl); - - throw Exception(Error::StsError, eerr, efile, efunc, __LINE__); - } - if (strcmp(version_buffer,"V6.01")) { - String eerr("version does not match"); - String efile(__FILE__); - String efunc(__FUNCTION__); - fclose(modelfl); - - throw Exception(Error::StsError, eerr, efile, efunc, __LINE__); - } - /* read version number */ - int version = 0; - if (!fread (&version,sizeof(int),1,modelfl)) - { - fclose(modelfl); - throw Exception(); - } - if (version < 200) - { - String eerr("version does not match"); - String efile(__FILE__); - String efunc(__FUNCTION__); - fclose(modelfl); - throw Exception(); - } - int kernel_type; - size_t nread; - nread=fread(&(kernel_type),sizeof(int),1,modelfl); - - {// ignore these - int poly_degree; - nread=fread(&(poly_degree),sizeof(int),1,modelfl); - - double rbf_gamma; - nread=fread(&(rbf_gamma),sizeof(double), 1, modelfl); - double coef_lin; - nread=fread(&(coef_lin),sizeof(double),1,modelfl); - double coef_const; - nread=fread(&(coef_const),sizeof(double),1,modelfl); - int l; - nread=fread(&l,sizeof(int),1,modelfl); - CV_Assert(l >= 0 && l < 0xFFFF); - char* custom = new char[l]; - nread=fread(custom,sizeof(char),l,modelfl); - delete[] custom; - } - int totwords; - nread=fread(&(totwords),sizeof(int),1,modelfl); - {// ignore these - int totdoc; - nread=fread(&(totdoc),sizeof(int),1,modelfl); - int sv_num; - nread=fread(&(sv_num), sizeof(int),1,modelfl); - } - - double linearbias; - nread=fread(&linearbias, sizeof(double), 1, modelfl); - - std::vector detector; - detector.clear(); - if (kernel_type == 0) { /* linear kernel */ - /* save linear wts also */ - CV_Assert(totwords + 1 > 0 && totwords < 0xFFFF); - double *linearwt = new double[totwords+1]; - int length = totwords; - nread = fread(linearwt, sizeof(double), totwords + 1, modelfl); - if (nread != static_cast(length) + 1) { - delete[] linearwt; - fclose(modelfl); - throw Exception(); - } - - for (int i = 0; i < length; i++) - detector.push_back((float)linearwt[i]); - - detector.push_back((float)-linearbias); - setSVMDetector(detector); - delete[] linearwt; - } else { - fclose(modelfl); - throw Exception(); - } - fclose(modelfl); - } - - void HOGDescriptor::groupRectangles(std::vector& rectList, std::vector& weights, int groupThreshold, double eps) const - { - CV_INSTRUMENT_REGION(); - - if (groupThreshold <= 0 || rectList.empty()) - { - return; - } - - CV_Assert(rectList.size() == weights.size()); - - std::vector labels; - int nclasses = partition(rectList, labels, SimilarRects(eps)); - - std::vector> rrects(nclasses); - std::vector numInClass(nclasses, 0); - std::vector foundWeights(nclasses, -std::numeric_limits::max()); - int i, j, nlabels = (int)labels.size(); - - for (i = 0; i < nlabels; i++) - { - int cls = labels[i]; - rrects[cls].x += rectList[i].x; - rrects[cls].y += rectList[i].y; - rrects[cls].width += rectList[i].width; - rrects[cls].height += rectList[i].height; - foundWeights[cls] = max(foundWeights[cls], weights[i]); - numInClass[cls]++; - } - - for (i = 0; i < nclasses; i++) - { - // find the average of all ROI in the cluster - cv::Rect_ r = rrects[i]; - double s = 1.0/numInClass[i]; - rrects[i] = cv::Rect_(cv::saturate_cast(r.x*s), - cv::saturate_cast(r.y*s), - cv::saturate_cast(r.width*s), - cv::saturate_cast(r.height*s)); - } - - rectList.clear(); - weights.clear(); - - for (i = 0; i < nclasses; i++) - { - cv::Rect r1 = rrects[i]; - int n1 = numInClass[i]; - double w1 = foundWeights[i]; - if (n1 <= groupThreshold) - continue; - // filter out small rectangles inside large rectangles - for (j = 0; j < nclasses; j++) - { - int n2 = numInClass[j]; - - if (j == i || n2 <= groupThreshold) - continue; - - cv::Rect r2 = rrects[j]; - - int dx = cv::saturate_cast( r2.width * eps ); - int dy = cv::saturate_cast( r2.height * eps ); - - if (r1.x >= r2.x - dx && - r1.y >= r2.y - dy && - r1.x + r1.width <= r2.x + r2.width + dx && - r1.y + r1.height <= r2.y + r2.height + dy && - (n2 > std::max(3, n1) || n1 < 3)) - break; - } - - if (j == nclasses) - { - rectList.push_back(r1); - weights.push_back(w1); - } - } - } -} diff --git a/test/bug-hunting/cve/CVE-2019-15939/opencv2/objdetect.hpp b/test/bug-hunting/cve/CVE-2019-15939/opencv2/objdetect.hpp deleted file mode 100644 index 097c5923af2..00000000000 --- a/test/bug-hunting/cve/CVE-2019-15939/opencv2/objdetect.hpp +++ /dev/null @@ -1,753 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2009, Willow Garage Inc., all rights reserved. -// Copyright (C) 2013, OpenCV Foundation, all rights reserved. -// Third party copyrights are property of their respective owners. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's 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. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. -// -//M*/ - -#ifndef OPENCV_OBJDETECT_HPP -#define OPENCV_OBJDETECT_HPP - -#include "opencv2/core.hpp" - -/** -@defgroup objdetect Object Detection - -Haar Feature-based Cascade Classifier for Object Detection ----------------------------------------------------------- - -The object detector described below has been initially proposed by Paul Viola @cite Viola01 and -improved by Rainer Lienhart @cite Lienhart02 . - -First, a classifier (namely a *cascade of boosted classifiers working with haar-like features*) is -trained with a few hundred sample views of a particular object (i.e., a face or a car), called -positive examples, that are scaled to the same size (say, 20x20), and negative examples - arbitrary -images of the same size. - -After a classifier is trained, it can be applied to a region of interest (of the same size as used -during the training) in an input image. The classifier outputs a "1" if the region is likely to show -the object (i.e., face/car), and "0" otherwise. To search for the object in the whole image one can -move the search window across the image and check every location using the classifier. The -classifier is designed so that it can be easily "resized" in order to be able to find the objects of -interest at different sizes, which is more efficient than resizing the image itself. So, to find an -object of an unknown size in the image the scan procedure should be done several times at different -scales. - -The word "cascade" in the classifier name means that the resultant classifier consists of several -simpler classifiers (*stages*) that are applied subsequently to a region of interest until at some -stage the candidate is rejected or all the stages are passed. The word "boosted" means that the -classifiers at every stage of the cascade are complex themselves and they are built out of basic -classifiers using one of four different boosting techniques (weighted voting). Currently Discrete -Adaboost, Real Adaboost, Gentle Adaboost and Logitboost are supported. The basic classifiers are -decision-tree classifiers with at least 2 leaves. Haar-like features are the input to the basic -classifiers, and are calculated as described below. The current algorithm uses the following -Haar-like features: - -![image](pics/haarfeatures.png) - -The feature used in a particular classifier is specified by its shape (1a, 2b etc.), position within -the region of interest and the scale (this scale is not the same as the scale used at the detection -stage, though these two scales are multiplied). For example, in the case of the third line feature -(2c) the response is calculated as the difference between the sum of image pixels under the -rectangle covering the whole feature (including the two white stripes and the black stripe in the -middle) and the sum of the image pixels under the black stripe multiplied by 3 in order to -compensate for the differences in the size of areas. The sums of pixel values over a rectangular -regions are calculated rapidly using integral images (see below and the integral description). - -To see the object detector at work, have a look at the facedetect demo: - - -The following reference is for the detection part only. There is a separate application called -opencv_traincascade that can train a cascade of boosted classifiers from a set of samples. - -@note In the new C++ interface it is also possible to use LBP (local binary pattern) features in -addition to Haar-like features. .. [Viola01] Paul Viola and Michael J. Jones. Rapid Object Detection -using a Boosted Cascade of Simple Features. IEEE CVPR, 2001. The paper is available online at - - -@{ - @defgroup objdetect_c C API -@} - */ - -typedef struct CvHaarClassifierCascade CvHaarClassifierCascade; - -namespace cv -{ - -//! @addtogroup objdetect -//! @{ - -///////////////////////////// Object Detection //////////////////////////// - -//! class for grouping object candidates, detected by Cascade Classifier, HOG etc. -//! instance of the class is to be passed to cv::partition (see cxoperations.hpp) -class CV_EXPORTS SimilarRects -{ -public: - SimilarRects(double _eps) : eps(_eps) {} - inline bool operator()(const Rect& r1, const Rect& r2) const - { - double delta = eps * ((std::min)(r1.width, r2.width) + (std::min)(r1.height, r2.height)) * 0.5; - return std::abs(r1.x - r2.x) <= delta && - std::abs(r1.y - r2.y) <= delta && - std::abs(r1.x + r1.width - r2.x - r2.width) <= delta && - std::abs(r1.y + r1.height - r2.y - r2.height) <= delta; - } - double eps; -}; - -/** @brief Groups the object candidate rectangles. - -@param rectList Input/output vector of rectangles. Output vector includes retained and grouped -rectangles. (The Python list is not modified in place.) -@param groupThreshold Minimum possible number of rectangles minus 1. The threshold is used in a -group of rectangles to retain it. -@param eps Relative difference between sides of the rectangles to merge them into a group. - -The function is a wrapper for the generic function partition . It clusters all the input rectangles -using the rectangle equivalence criteria that combines rectangles with similar sizes and similar -locations. The similarity is defined by eps. When eps=0 , no clustering is done at all. If -\f$\texttt{eps}\rightarrow +\inf\f$ , all the rectangles are put in one cluster. Then, the small -clusters containing less than or equal to groupThreshold rectangles are rejected. In each other -cluster, the average rectangle is computed and put into the output rectangle list. - */ -CV_EXPORTS void groupRectangles(std::vector& rectList, int groupThreshold, double eps = 0.2); -/** @overload */ -CV_EXPORTS_W void groupRectangles(CV_IN_OUT std::vector& rectList, CV_OUT std::vector& weights, - int groupThreshold, double eps = 0.2); -/** @overload */ -CV_EXPORTS void groupRectangles(std::vector& rectList, int groupThreshold, - double eps, std::vector* weights, std::vector* levelWeights ); -/** @overload */ -CV_EXPORTS void groupRectangles(std::vector& rectList, std::vector& rejectLevels, - std::vector& levelWeights, int groupThreshold, double eps = 0.2); -/** @overload */ -CV_EXPORTS void groupRectangles_meanshift(std::vector& rectList, std::vector& foundWeights, - std::vector& foundScales, - double detectThreshold = 0.0, Size winDetSize = Size(64, 128)); - -template<> struct DefaultDeleter{ CV_EXPORTS void operator ()(CvHaarClassifierCascade* obj) const; }; - -enum { CASCADE_DO_CANNY_PRUNING = 1, - CASCADE_SCALE_IMAGE = 2, - CASCADE_FIND_BIGGEST_OBJECT = 4, - CASCADE_DO_ROUGH_SEARCH = 8 - }; - -class CV_EXPORTS_W BaseCascadeClassifier : public Algorithm -{ -public: - virtual ~BaseCascadeClassifier(); - virtual bool empty() const CV_OVERRIDE = 0; - virtual bool load( const String& filename ) = 0; - virtual void detectMultiScale( InputArray image, - CV_OUT std::vector& objects, - double scaleFactor, - int minNeighbors, int flags, - Size minSize, Size maxSize ) = 0; - - virtual void detectMultiScale( InputArray image, - CV_OUT std::vector& objects, - CV_OUT std::vector& numDetections, - double scaleFactor, - int minNeighbors, int flags, - Size minSize, Size maxSize ) = 0; - - virtual void detectMultiScale( InputArray image, - CV_OUT std::vector& objects, - CV_OUT std::vector& rejectLevels, - CV_OUT std::vector& levelWeights, - double scaleFactor, - int minNeighbors, int flags, - Size minSize, Size maxSize, - bool outputRejectLevels ) = 0; - - virtual bool isOldFormatCascade() const = 0; - virtual Size getOriginalWindowSize() const = 0; - virtual int getFeatureType() const = 0; - virtual void* getOldCascade() = 0; - - class CV_EXPORTS MaskGenerator - { - public: - virtual ~MaskGenerator() {} - virtual Mat generateMask(const Mat& src)=0; - virtual void initializeMask(const Mat& /*src*/) { } - }; - virtual void setMaskGenerator(const Ptr& maskGenerator) = 0; - virtual Ptr getMaskGenerator() = 0; -}; - -/** @example samples/cpp/facedetect.cpp -This program demonstrates usage of the Cascade classifier class -\image html Cascade_Classifier_Tutorial_Result_Haar.jpg "Sample screenshot" width=321 height=254 -*/ -/** @brief Cascade classifier class for object detection. - */ -class CV_EXPORTS_W CascadeClassifier -{ -public: - CV_WRAP CascadeClassifier(); - /** @brief Loads a classifier from a file. - - @param filename Name of the file from which the classifier is loaded. - */ - CV_WRAP CascadeClassifier(const String& filename); - ~CascadeClassifier(); - /** @brief Checks whether the classifier has been loaded. - */ - CV_WRAP bool empty() const; - /** @brief Loads a classifier from a file. - - @param filename Name of the file from which the classifier is loaded. The file may contain an old - HAAR classifier trained by the haartraining application or a new cascade classifier trained by the - traincascade application. - */ - CV_WRAP bool load( const String& filename ); - /** @brief Reads a classifier from a FileStorage node. - - @note The file may contain a new cascade classifier (trained traincascade application) only. - */ - CV_WRAP bool read( const FileNode& node ); - - /** @brief Detects objects of different sizes in the input image. The detected objects are returned as a list - of rectangles. - - @param image Matrix of the type CV_8U containing an image where objects are detected. - @param objects Vector of rectangles where each rectangle contains the detected object, the - rectangles may be partially outside the original image. - @param scaleFactor Parameter specifying how much the image size is reduced at each image scale. - @param minNeighbors Parameter specifying how many neighbors each candidate rectangle should have - to retain it. - @param flags Parameter with the same meaning for an old cascade as in the function - cvHaarDetectObjects. It is not used for a new cascade. - @param minSize Minimum possible object size. Objects smaller than that are ignored. - @param maxSize Maximum possible object size. Objects larger than that are ignored. If `maxSize == minSize` model is evaluated on single scale. - - The function is parallelized with the TBB library. - - @note - - (Python) A face detection example using cascade classifiers can be found at - opencv_source_code/samples/python/facedetect.py - */ - CV_WRAP void detectMultiScale( InputArray image, - CV_OUT std::vector& objects, - double scaleFactor = 1.1, - int minNeighbors = 3, int flags = 0, - Size minSize = Size(), - Size maxSize = Size() ); - - /** @overload - @param image Matrix of the type CV_8U containing an image where objects are detected. - @param objects Vector of rectangles where each rectangle contains the detected object, the - rectangles may be partially outside the original image. - @param numDetections Vector of detection numbers for the corresponding objects. An object's number - of detections is the number of neighboring positively classified rectangles that were joined - together to form the object. - @param scaleFactor Parameter specifying how much the image size is reduced at each image scale. - @param minNeighbors Parameter specifying how many neighbors each candidate rectangle should have - to retain it. - @param flags Parameter with the same meaning for an old cascade as in the function - cvHaarDetectObjects. It is not used for a new cascade. - @param minSize Minimum possible object size. Objects smaller than that are ignored. - @param maxSize Maximum possible object size. Objects larger than that are ignored. If `maxSize == minSize` model is evaluated on single scale. - */ - CV_WRAP_AS(detectMultiScale2) void detectMultiScale( InputArray image, - CV_OUT std::vector& objects, - CV_OUT std::vector& numDetections, - double scaleFactor=1.1, - int minNeighbors=3, int flags=0, - Size minSize=Size(), - Size maxSize=Size() ); - - /** @overload - This function allows you to retrieve the final stage decision certainty of classification. - For this, one needs to set `outputRejectLevels` on true and provide the `rejectLevels` and `levelWeights` parameter. - For each resulting detection, `levelWeights` will then contain the certainty of classification at the final stage. - This value can then be used to separate strong from weaker classifications. - - A code sample on how to use it efficiently can be found below: - @code - Mat img; - vector weights; - vector levels; - vector detections; - CascadeClassifier model("/path/to/your/model.xml"); - model.detectMultiScale(img, detections, levels, weights, 1.1, 3, 0, Size(), Size(), true); - cerr << "Detection " << detections[0] << " with weight " << weights[0] << endl; - @endcode - */ - CV_WRAP_AS(detectMultiScale3) void detectMultiScale( InputArray image, - CV_OUT std::vector& objects, - CV_OUT std::vector& rejectLevels, - CV_OUT std::vector& levelWeights, - double scaleFactor = 1.1, - int minNeighbors = 3, int flags = 0, - Size minSize = Size(), - Size maxSize = Size(), - bool outputRejectLevels = false ); - - CV_WRAP bool isOldFormatCascade() const; - CV_WRAP Size getOriginalWindowSize() const; - CV_WRAP int getFeatureType() const; - void* getOldCascade(); - - CV_WRAP static bool convert(const String& oldcascade, const String& newcascade); - - void setMaskGenerator(const Ptr& maskGenerator); - Ptr getMaskGenerator(); - - Ptr cc; -}; - -CV_EXPORTS Ptr createFaceDetectionMaskGenerator(); - -//////////////// HOG (Histogram-of-Oriented-Gradients) Descriptor and Object Detector ////////////// - -//! struct for detection region of interest (ROI) -struct DetectionROI -{ - //! scale(size) of the bounding box - double scale; - //! set of requested locations to be evaluated - std::vector locations; - //! vector that will contain confidence values for each location - std::vector confidences; -}; - -/**@brief Implementation of HOG (Histogram of Oriented Gradients) descriptor and object detector. - -the HOG descriptor algorithm introduced by Navneet Dalal and Bill Triggs @cite Dalal2005 . - -useful links: - -https://hal.inria.fr/inria-00548512/document/ - -https://en.wikipedia.org/wiki/Histogram_of_oriented_gradients - -https://software.intel.com/en-us/ipp-dev-reference-histogram-of-oriented-gradients-hog-descriptor - -http://www.learnopencv.com/histogram-of-oriented-gradients - -http://www.learnopencv.com/handwritten-digits-classification-an-opencv-c-python-tutorial - - */ -struct CV_EXPORTS_W HOGDescriptor -{ -public: - enum HistogramNormType { L2Hys = 0 //!< Default histogramNormType - }; - enum { DEFAULT_NLEVELS = 64 //!< Default nlevels value. - }; - enum DescriptorStorageFormat { DESCR_FORMAT_COL_BY_COL, DESCR_FORMAT_ROW_BY_ROW }; - - /**@brief Creates the HOG descriptor and detector with default params. - - aqual to HOGDescriptor(Size(64,128), Size(16,16), Size(8,8), Size(8,8), 9 ) - */ - CV_WRAP HOGDescriptor() : winSize(64,128), blockSize(16,16), blockStride(8,8), - cellSize(8,8), nbins(9), derivAperture(1), winSigma(-1), - histogramNormType(HOGDescriptor::L2Hys), L2HysThreshold(0.2), gammaCorrection(true), - free_coef(-1.f), nlevels(HOGDescriptor::DEFAULT_NLEVELS), signedGradient(false) - {} - - /** @overload - @param _winSize sets winSize with given value. - @param _blockSize sets blockSize with given value. - @param _blockStride sets blockStride with given value. - @param _cellSize sets cellSize with given value. - @param _nbins sets nbins with given value. - @param _derivAperture sets derivAperture with given value. - @param _winSigma sets winSigma with given value. - @param _histogramNormType sets histogramNormType with given value. - @param _L2HysThreshold sets L2HysThreshold with given value. - @param _gammaCorrection sets gammaCorrection with given value. - @param _nlevels sets nlevels with given value. - @param _signedGradient sets signedGradient with given value. - */ - CV_WRAP HOGDescriptor(Size _winSize, Size _blockSize, Size _blockStride, - Size _cellSize, int _nbins, int _derivAperture=1, double _winSigma=-1, - HOGDescriptor::HistogramNormType _histogramNormType=HOGDescriptor::L2Hys, - double _L2HysThreshold=0.2, bool _gammaCorrection=false, - int _nlevels=HOGDescriptor::DEFAULT_NLEVELS, bool _signedGradient=false) - : winSize(_winSize), blockSize(_blockSize), blockStride(_blockStride), cellSize(_cellSize), - nbins(_nbins), derivAperture(_derivAperture), winSigma(_winSigma), - histogramNormType(_histogramNormType), L2HysThreshold(_L2HysThreshold), - gammaCorrection(_gammaCorrection), free_coef(-1.f), nlevels(_nlevels), signedGradient(_signedGradient) - {} - - /** @overload - @param filename The file name containing HOGDescriptor properties and coefficients for the linear SVM classifier. - */ - CV_WRAP HOGDescriptor(const String& filename) - { - load(filename); - } - - /** @overload - @param d the HOGDescriptor which cloned to create a new one. - */ - HOGDescriptor(const HOGDescriptor& d) - { - d.copyTo(*this); - } - - /**@brief Default destructor. - */ - virtual ~HOGDescriptor() {} - - /**@brief Returns the number of coefficients required for the classification. - */ - CV_WRAP size_t getDescriptorSize() const; - - /** @brief Checks if detector size equal to descriptor size. - */ - CV_WRAP bool checkDetectorSize() const; - - /** @brief Returns winSigma value - */ - CV_WRAP double getWinSigma() const; - - /**@example samples/cpp/peopledetect.cpp - */ - /**@brief Sets coefficients for the linear SVM classifier. - @param svmdetector coefficients for the linear SVM classifier. - */ - CV_WRAP virtual void setSVMDetector(InputArray svmdetector); - - /** @brief Reads HOGDescriptor parameters from a cv::FileNode. - @param fn File node - */ - virtual bool read(FileNode& fn); - - /** @brief Stores HOGDescriptor parameters in a cv::FileStorage. - @param fs File storage - @param objname Object name - */ - virtual void write(FileStorage& fs, const String& objname) const; - - /** @brief loads HOGDescriptor parameters and coefficients for the linear SVM classifier from a file. - @param filename Path of the file to read. - @param objname The optional name of the node to read (if empty, the first top-level node will be used). - */ - CV_WRAP virtual bool load(const String& filename, const String& objname = String()); - - /** @brief saves HOGDescriptor parameters and coefficients for the linear SVM classifier to a file - @param filename File name - @param objname Object name - */ - CV_WRAP virtual void save(const String& filename, const String& objname = String()) const; - - /** @brief clones the HOGDescriptor - @param c cloned HOGDescriptor - */ - virtual void copyTo(HOGDescriptor& c) const; - - /**@example samples/cpp/train_HOG.cpp - */ - /** @brief Computes HOG descriptors of given image. - @param img Matrix of the type CV_8U containing an image where HOG features will be calculated. - @param descriptors Matrix of the type CV_32F - @param winStride Window stride. It must be a multiple of block stride. - @param padding Padding - @param locations Vector of Point - */ - CV_WRAP virtual void compute(InputArray img, - CV_OUT std::vector& descriptors, - Size winStride = Size(), Size padding = Size(), - const std::vector& locations = std::vector()) const; - - /** @brief Performs object detection without a multi-scale window. - @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. - @param foundLocations Vector of point where each point contains left-top corner point of detected object boundaries. - @param weights Vector that will contain confidence values for each detected object. - @param hitThreshold Threshold for the distance between features and SVM classifying plane. - Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient). - But if the free coefficient is omitted (which is allowed), you can specify it manually here. - @param winStride Window stride. It must be a multiple of block stride. - @param padding Padding - @param searchLocations Vector of Point includes set of requested locations to be evaluated. - */ - CV_WRAP virtual void detect(InputArray img, CV_OUT std::vector& foundLocations, - CV_OUT std::vector& weights, - double hitThreshold = 0, Size winStride = Size(), - Size padding = Size(), - const std::vector& searchLocations = std::vector()) const; - - /** @brief Performs object detection without a multi-scale window. - @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. - @param foundLocations Vector of point where each point contains left-top corner point of detected object boundaries. - @param hitThreshold Threshold for the distance between features and SVM classifying plane. - Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient). - But if the free coefficient is omitted (which is allowed), you can specify it manually here. - @param winStride Window stride. It must be a multiple of block stride. - @param padding Padding - @param searchLocations Vector of Point includes locations to search. - */ - virtual void detect(InputArray img, CV_OUT std::vector& foundLocations, - double hitThreshold = 0, Size winStride = Size(), - Size padding = Size(), - const std::vector& searchLocations=std::vector()) const; - - /** @brief Detects objects of different sizes in the input image. The detected objects are returned as a list - of rectangles. - @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. - @param foundLocations Vector of rectangles where each rectangle contains the detected object. - @param foundWeights Vector that will contain confidence values for each detected object. - @param hitThreshold Threshold for the distance between features and SVM classifying plane. - Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient). - But if the free coefficient is omitted (which is allowed), you can specify it manually here. - @param winStride Window stride. It must be a multiple of block stride. - @param padding Padding - @param scale Coefficient of the detection window increase. - @param finalThreshold Final threshold - @param useMeanshiftGrouping indicates grouping algorithm - */ - CV_WRAP virtual void detectMultiScale(InputArray img, CV_OUT std::vector& foundLocations, - CV_OUT std::vector& foundWeights, double hitThreshold = 0, - Size winStride = Size(), Size padding = Size(), double scale = 1.05, - double finalThreshold = 2.0,bool useMeanshiftGrouping = false) const; - - /** @brief Detects objects of different sizes in the input image. The detected objects are returned as a list - of rectangles. - @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. - @param foundLocations Vector of rectangles where each rectangle contains the detected object. - @param hitThreshold Threshold for the distance between features and SVM classifying plane. - Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient). - But if the free coefficient is omitted (which is allowed), you can specify it manually here. - @param winStride Window stride. It must be a multiple of block stride. - @param padding Padding - @param scale Coefficient of the detection window increase. - @param finalThreshold Final threshold - @param useMeanshiftGrouping indicates grouping algorithm - */ - virtual void detectMultiScale(InputArray img, CV_OUT std::vector& foundLocations, - double hitThreshold = 0, Size winStride = Size(), - Size padding = Size(), double scale = 1.05, - double finalThreshold = 2.0, bool useMeanshiftGrouping = false) const; - - /** @brief Computes gradients and quantized gradient orientations. - @param img Matrix contains the image to be computed - @param grad Matrix of type CV_32FC2 contains computed gradients - @param angleOfs Matrix of type CV_8UC2 contains quantized gradient orientations - @param paddingTL Padding from top-left - @param paddingBR Padding from bottom-right - */ - CV_WRAP virtual void computeGradient(InputArray img, InputOutputArray grad, InputOutputArray angleOfs, - Size paddingTL = Size(), Size paddingBR = Size()) const; - - /** @brief Returns coefficients of the classifier trained for people detection (for 64x128 windows). - */ - CV_WRAP static std::vector getDefaultPeopleDetector(); - - /**@example samples/tapi/hog.cpp - */ - /** @brief Returns coefficients of the classifier trained for people detection (for 48x96 windows). - */ - CV_WRAP static std::vector getDaimlerPeopleDetector(); - - //! Detection window size. Align to block size and block stride. Default value is Size(64,128). - CV_PROP Size winSize; - - //! Block size in pixels. Align to cell size. Default value is Size(16,16). - CV_PROP Size blockSize; - - //! Block stride. It must be a multiple of cell size. Default value is Size(8,8). - CV_PROP Size blockStride; - - //! Cell size. Default value is Size(8,8). - CV_PROP Size cellSize; - - //! Number of bins used in the calculation of histogram of gradients. Default value is 9. - CV_PROP int nbins; - - //! not documented - CV_PROP int derivAperture; - - //! Gaussian smoothing window parameter. - CV_PROP double winSigma; - - //! histogramNormType - CV_PROP HOGDescriptor::HistogramNormType histogramNormType; - - //! L2-Hys normalization method shrinkage. - CV_PROP double L2HysThreshold; - - //! Flag to specify whether the gamma correction preprocessing is required or not. - CV_PROP bool gammaCorrection; - - //! coefficients for the linear SVM classifier. - CV_PROP std::vector svmDetector; - - //! coefficients for the linear SVM classifier used when OpenCL is enabled - UMat oclSvmDetector; - - //! not documented - float free_coef; - - //! Maximum number of detection window increases. Default value is 64 - CV_PROP int nlevels; - - //! Indicates signed gradient will be used or not - CV_PROP bool signedGradient; - - /** @brief evaluate specified ROI and return confidence value for each location - @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. - @param locations Vector of Point - @param foundLocations Vector of Point where each Point is detected object's top-left point. - @param confidences confidences - @param hitThreshold Threshold for the distance between features and SVM classifying plane. Usually - it is 0 and should be specified in the detector coefficients (as the last free coefficient). But if - the free coefficient is omitted (which is allowed), you can specify it manually here - @param winStride winStride - @param padding padding - */ - virtual void detectROI(InputArray img, const std::vector &locations, - CV_OUT std::vector& foundLocations, CV_OUT std::vector& confidences, - double hitThreshold = 0, cv::Size winStride = Size(), - cv::Size padding = Size()) const; - - /** @brief evaluate specified ROI and return confidence value for each location in multiple scales - @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. - @param foundLocations Vector of rectangles where each rectangle contains the detected object. - @param locations Vector of DetectionROI - @param hitThreshold Threshold for the distance between features and SVM classifying plane. Usually it is 0 and should be specified - in the detector coefficients (as the last free coefficient). But if the free coefficient is omitted (which is allowed), you can specify it manually here. - @param groupThreshold Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it. - */ - virtual void detectMultiScaleROI(InputArray img, - CV_OUT std::vector& foundLocations, - std::vector& locations, - double hitThreshold = 0, - int groupThreshold = 0) const; - - /** @brief Groups the object candidate rectangles. - @param rectList Input/output vector of rectangles. Output vector includes retained and grouped rectangles. (The Python list is not modified in place.) - @param weights Input/output vector of weights of rectangles. Output vector includes weights of retained and grouped rectangles. (The Python list is not modified in place.) - @param groupThreshold Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it. - @param eps Relative difference between sides of the rectangles to merge them into a group. - */ - void groupRectangles(std::vector& rectList, std::vector& weights, int groupThreshold, double eps) const; -}; - -class CV_EXPORTS_W QRCodeDetector -{ -public: - CV_WRAP QRCodeDetector(); - ~QRCodeDetector(); - - /** @brief sets the epsilon used during the horizontal scan of QR code stop marker detection. - @param epsX Epsilon neighborhood, which allows you to determine the horizontal pattern - of the scheme 1:1:3:1:1 according to QR code standard. - */ - CV_WRAP void setEpsX(double epsX); - /** @brief sets the epsilon used during the vertical scan of QR code stop marker detection. - @param epsY Epsilon neighborhood, which allows you to determine the vertical pattern - of the scheme 1:1:3:1:1 according to QR code standard. - */ - CV_WRAP void setEpsY(double epsY); - - /** @brief Detects QR code in image and returns the quadrangle containing the code. - @param img grayscale or color (BGR) image containing (or not) QR code. - @param points Output vector of vertices of the minimum-area quadrangle containing the code. - */ - CV_WRAP bool detect(InputArray img, OutputArray points) const; - - /** @brief Decodes QR code in image once it's found by the detect() method. - - Returns UTF8-encoded output string or empty string if the code cannot be decoded. - @param img grayscale or color (BGR) image containing QR code. - @param points Quadrangle vertices found by detect() method (or some other algorithm). - @param straight_qrcode The optional output image containing rectified and binarized QR code - */ - CV_WRAP std::string decode(InputArray img, InputArray points, OutputArray straight_qrcode = noArray()); - - /** @brief Both detects and decodes QR code - - @param img grayscale or color (BGR) image containing QR code. - @param points optional output array of vertices of the found QR code quadrangle. Will be empty if not found. - @param straight_qrcode The optional output image containing rectified and binarized QR code - */ - CV_WRAP std::string detectAndDecode(InputArray img, OutputArray points=noArray(), - OutputArray straight_qrcode = noArray()); - /** @brief Detects QR codes in image and returns the vector of the quadrangles containing the codes. - @param img grayscale or color (BGR) image containing (or not) QR codes. - @param points Output vector of vector of vertices of the minimum-area quadrangle containing the codes. - */ - CV_WRAP - bool detectMulti(InputArray img, OutputArray points) const; - - /** @brief Decodes QR codes in image once it's found by the detect() method. - @param img grayscale or color (BGR) image containing QR codes. - @param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded. - @param points vector of Quadrangle vertices found by detect() method (or some other algorithm). - @param straight_qrcode The optional output vector of images containing rectified and binarized QR codes - */ - CV_WRAP - bool decodeMulti( - InputArray img, InputArray points, - CV_OUT std::vector& decoded_info, - OutputArrayOfArrays straight_qrcode = noArray() - ) const; - - /** @brief Both detects and decodes QR codes - @param img grayscale or color (BGR) image containing QR codes. - @param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded. - @param points optional output vector of vertices of the found QR code quadrangles. Will be empty if not found. - @param straight_qrcode The optional output vector of images containing rectified and binarized QR codes - */ - CV_WRAP - bool detectAndDecodeMulti( - InputArray img, CV_OUT std::vector& decoded_info, - OutputArray points = noArray(), - OutputArrayOfArrays straight_qrcode = noArray() - ) const; - -protected: - struct Impl; - Ptr p; -}; - -//! @} objdetect -} - -#include "opencv2/objdetect/detection_based_tracker.hpp" - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-15939/precomp.hpp b/test/bug-hunting/cve/CVE-2019-15939/precomp.hpp deleted file mode 100644 index cbefc396be9..00000000000 --- a/test/bug-hunting/cve/CVE-2019-15939/precomp.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2009, Willow Garage Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's 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. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. -// -//M*/ - -#ifndef __OPENCV_PRECOMP_H__ -#define __OPENCV_PRECOMP_H__ - -#include "opencv2/objdetect.hpp" -#include "opencv2/imgproc.hpp" - -#include "opencv2/core/utility.hpp" -#include "opencv2/core/ocl.hpp" -#include "opencv2/core/private.hpp" - -#endif diff --git a/test/bug-hunting/cve/CVE-2019-16168/expected.txt b/test/bug-hunting/cve/CVE-2019-16168/expected.txt deleted file mode 100644 index 4212afe12cb..00000000000 --- a/test/bug-hunting/cve/CVE-2019-16168/expected.txt +++ /dev/null @@ -1 +0,0 @@ -where.c:2673:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-16168/where.c b/test/bug-hunting/cve/CVE-2019-16168/where.c deleted file mode 100644 index ea0bfe03358..00000000000 --- a/test/bug-hunting/cve/CVE-2019-16168/where.c +++ /dev/null @@ -1,5388 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This module contains C code that generates VDBE code used to process -** the WHERE clause of SQL statements. This module is responsible for -** generating the code that loops through a table looking for applicable -** rows. Indices are selected and used to speed the search when doing -** so is applicable. Because this module is responsible for selecting -** indices, you might also think of this module as the "query optimizer". -*/ -#include "sqliteInt.h" -#include "whereInt.h" - -/* -** Extra information appended to the end of sqlite3_index_info but not -** visible to the xBestIndex function, at least not directly. The -** sqlite3_vtab_collation() interface knows how to reach it, however. -** -** This object is not an API and can be changed from one release to the -** next. As long as allocateIndexInfo() and sqlite3_vtab_collation() -** agree on the structure, all will be well. -*/ -typedef struct HiddenIndexInfo HiddenIndexInfo; -struct HiddenIndexInfo { - WhereClause *pWC; /* The Where clause being analyzed */ - Parse *pParse; /* The parsing context */ -}; - -/* Forward declaration of methods */ -static int whereLoopResize(sqlite3*, WhereLoop*, int); - -/* Test variable that can be set to enable WHERE tracing */ -#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) -/***/ int sqlite3WhereTrace = 0; -#endif - - -/* -** Return the estimated number of output rows from a WHERE clause -*/ -LogEst sqlite3WhereOutputRowCount(WhereInfo *pWInfo){ - return pWInfo->nRowOut; -} - -/* -** Return one of the WHERE_DISTINCT_xxxxx values to indicate how this -** WHERE clause returns outputs for DISTINCT processing. -*/ -int sqlite3WhereIsDistinct(WhereInfo *pWInfo){ - return pWInfo->eDistinct; -} - -/* -** Return TRUE if the WHERE clause returns rows in ORDER BY order. -** Return FALSE if the output needs to be sorted. -*/ -int sqlite3WhereIsOrdered(WhereInfo *pWInfo){ - return pWInfo->nOBSat; -} - -/* -** In the ORDER BY LIMIT optimization, if the inner-most loop is known -** to emit rows in increasing order, and if the last row emitted by the -** inner-most loop did not fit within the sorter, then we can skip all -** subsequent rows for the current iteration of the inner loop (because they -** will not fit in the sorter either) and continue with the second inner -** loop - the loop immediately outside the inner-most. -** -** When a row does not fit in the sorter (because the sorter already -** holds LIMIT+OFFSET rows that are smaller), then a jump is made to the -** label returned by this function. -** -** If the ORDER BY LIMIT optimization applies, the jump destination should -** be the continuation for the second-inner-most loop. If the ORDER BY -** LIMIT optimization does not apply, then the jump destination should -** be the continuation for the inner-most loop. -** -** It is always safe for this routine to return the continuation of the -** inner-most loop, in the sense that a correct answer will result. -** Returning the continuation the second inner loop is an optimization -** that might make the code run a little faster, but should not change -** the final answer. -*/ -int sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo){ - WhereLevel *pInner; - if (!pWInfo->bOrderedInnerLoop) { - /* The ORDER BY LIMIT optimization does not apply. Jump to the - ** continuation of the inner-most loop. */ - return pWInfo->iContinue; - } - pInner = &pWInfo->a[pWInfo->nLevel-1]; - assert( pInner->addrNxt!=0 ); - return pInner->addrNxt; -} - -/* -** Return the VDBE address or label to jump to in order to continue -** immediately with the next row of a WHERE clause. -*/ -int sqlite3WhereContinueLabel(WhereInfo *pWInfo){ - assert( pWInfo->iContinue!=0 ); - return pWInfo->iContinue; -} - -/* -** Return the VDBE address or label to jump to in order to break -** out of a WHERE loop. -*/ -int sqlite3WhereBreakLabel(WhereInfo *pWInfo){ - return pWInfo->iBreak; -} - -/* -** Return ONEPASS_OFF (0) if an UPDATE or DELETE statement is unable to -** operate directly on the rowis returned by a WHERE clause. Return -** ONEPASS_SINGLE (1) if the statement can operation directly because only -** a single row is to be changed. Return ONEPASS_MULTI (2) if the one-pass -** optimization can be used on multiple -** -** If the ONEPASS optimization is used (if this routine returns true) -** then also write the indices of open cursors used by ONEPASS -** into aiCur[0] and aiCur[1]. iaCur[0] gets the cursor of the data -** table and iaCur[1] gets the cursor used by an auxiliary index. -** Either value may be -1, indicating that cursor is not used. -** Any cursors returned will have been opened for writing. -** -** aiCur[0] and aiCur[1] both get -1 if the where-clause logic is -** unable to use the ONEPASS optimization. -*/ -int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){ - memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2); -#ifdef WHERETRACE_ENABLED - if (sqlite3WhereTrace && pWInfo->eOnePass!=ONEPASS_OFF) { - sqlite3DebugPrintf("%s cursors: %d %d\n", - pWInfo->eOnePass==ONEPASS_SINGLE ? "ONEPASS_SINGLE" : "ONEPASS_MULTI", - aiCur[0], aiCur[1]); - } -#endif - return pWInfo->eOnePass; -} - -/* -** Move the content of pSrc into pDest -*/ -static void whereOrMove(WhereOrSet *pDest, WhereOrSet *pSrc){ - pDest->n = pSrc->n; - memcpy(pDest->a, pSrc->a, pDest->n*sizeof(pDest->a[0])); -} - -/* -** Try to insert a new prerequisite/cost entry into the WhereOrSet pSet. -** -** The new entry might overwrite an existing entry, or it might be -** appended, or it might be discarded. Do whatever is the right thing -** so that pSet keeps the N_OR_COST best entries seen so far. -*/ -static int whereOrInsert( - WhereOrSet *pSet, /* The WhereOrSet to be updated */ - Bitmask prereq, /* Prerequisites of the new entry */ - LogEst rRun, /* Run-cost of the new entry */ - LogEst nOut /* Number of outputs for the new entry */ - ){ - u16 i; - WhereOrCost *p; - for (i=pSet->n, p=pSet->a; i>0; i--, p++) { - if (rRun<=p->rRun && (prereq & p->prereq)==prereq) { - goto whereOrInsert_done; - } - if (p->rRun<=rRun && (p->prereq & prereq)==p->prereq) { - return 0; - } - } - if (pSet->na[pSet->n++]; - p->nOut = nOut; - } else { - p = pSet->a; - for (i=1; in; i++) { - if (p->rRun>pSet->a[i].rRun) p = pSet->a + i; - } - if (p->rRun<=rRun) return 0; - } -whereOrInsert_done: - p->prereq = prereq; - p->rRun = rRun; - if (p->nOut>nOut) p->nOut = nOut; - return 1; -} - -/* -** Return the bitmask for the given cursor number. Return 0 if -** iCursor is not in the set. -*/ -Bitmask sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){ - int i; - assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 ); - for (i=0; in; i++) { - if (pMaskSet->ix[i]==iCursor) { - return MASKBIT(i); - } - } - return 0; -} - -/* -** Create a new mask for cursor iCursor. -** -** There is one cursor per table in the FROM clause. The number of -** tables in the FROM clause is limited by a test early in the -** sqlite3WhereBegin() routine. So we know that the pMaskSet->ix[] -** array will never overflow. -*/ -static void createMask(WhereMaskSet *pMaskSet, int iCursor){ - assert( pMaskSet->n < ArraySize(pMaskSet->ix)); - pMaskSet->ix[pMaskSet->n++] = iCursor; -} - -/* -** Advance to the next WhereTerm that matches according to the criteria -** established when the pScan object was initialized by whereScanInit(). -** Return NULL if there are no more matching WhereTerms. -*/ -static WhereTerm *whereScanNext(WhereScan *pScan){ - int iCur; /* The cursor on the LHS of the term */ - i16 iColumn; /* The column on the LHS of the term. -1 for IPK */ - Expr *pX; /* An expression being tested */ - WhereClause *pWC; /* Shorthand for pScan->pWC */ - WhereTerm *pTerm; /* The term being tested */ - int k = pScan->k; /* Where to start scanning */ - - assert( pScan->iEquiv<=pScan->nEquiv ); - pWC = pScan->pWC; - while (1) { - iColumn = pScan->aiColumn[pScan->iEquiv-1]; - iCur = pScan->aiCur[pScan->iEquiv-1]; - assert( pWC!=0 ); - do{ - for (pTerm=pWC->a+k; knTerm; k++, pTerm++) { - if (pTerm->leftCursor==iCur - && pTerm->u.leftColumn==iColumn - && (iColumn!=XN_EXPR - || sqlite3ExprCompareSkip(pTerm->pExpr->pLeft, - pScan->pIdxExpr,iCur)==0) - && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) - ) { - if ((pTerm->eOperator & WO_EQUIV)!=0 - && pScan->nEquivaiCur) - && (pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight))->op==TK_COLUMN - ) { - int j; - for (j=0; jnEquiv; j++) { - if (pScan->aiCur[j]==pX->iTable - && pScan->aiColumn[j]==pX->iColumn) { - break; - } - } - if (j==pScan->nEquiv) { - pScan->aiCur[j] = pX->iTable; - pScan->aiColumn[j] = pX->iColumn; - pScan->nEquiv++; - } - } - if ((pTerm->eOperator & pScan->opMask)!=0) { - /* Verify the affinity and collating sequence match */ - if (pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0) { - CollSeq *pColl; - Parse *pParse = pWC->pWInfo->pParse; - pX = pTerm->pExpr; - if (!sqlite3IndexAffinityOk(pX, pScan->idxaff)) { - continue; - } - assert(pX->pLeft); - pColl = sqlite3BinaryCompareCollSeq(pParse, - pX->pLeft, pX->pRight); - if (pColl==0) pColl = pParse->db->pDfltColl; - if (sqlite3StrICmp(pColl->zName, pScan->zCollName)) { - continue; - } - } - if ((pTerm->eOperator & (WO_EQ|WO_IS))!=0 - && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN - && pX->iTable==pScan->aiCur[0] - && pX->iColumn==pScan->aiColumn[0] - ) { - testcase( pTerm->eOperator & WO_IS ); - continue; - } - pScan->pWC = pWC; - pScan->k = k+1; - return pTerm; - } - } - } - pWC = pWC->pOuter; - k = 0; - }while (pWC!=0); - if (pScan->iEquiv>=pScan->nEquiv) break; - pWC = pScan->pOrigWC; - k = 0; - pScan->iEquiv++; - } - return 0; -} - -/* -** This is whereScanInit() for the case of an index on an expression. -** It is factored out into a separate tail-recursion subroutine so that -** the normal whereScanInit() routine, which is a high-runner, does not -** need to push registers onto the stack as part of its prologue. -*/ -static SQLITE_NOINLINE WhereTerm *whereScanInitIndexExpr(WhereScan *pScan){ - pScan->idxaff = sqlite3ExprAffinity(pScan->pIdxExpr); - return whereScanNext(pScan); -} - -/* -** Initialize a WHERE clause scanner object. Return a pointer to the -** first match. Return NULL if there are no matches. -** -** The scanner will be searching the WHERE clause pWC. It will look -** for terms of the form "X " where X is column iColumn of table -** iCur. Or if pIdx!=0 then X is column iColumn of index pIdx. pIdx -** must be one of the indexes of table iCur. -** -** The must be one of the operators described by opMask. -** -** If the search is for X and the WHERE clause contains terms of the -** form X=Y then this routine might also return terms of the form -** "Y ". The number of levels of transitivity is limited, -** but is enough to handle most commonly occurring SQL statements. -** -** If X is not the INTEGER PRIMARY KEY then X must be compatible with -** index pIdx. -*/ -static WhereTerm *whereScanInit( - WhereScan *pScan, /* The WhereScan object being initialized */ - WhereClause *pWC, /* The WHERE clause to be scanned */ - int iCur, /* Cursor to scan for */ - int iColumn, /* Column to scan for */ - u32 opMask, /* Operator(s) to scan for */ - Index *pIdx /* Must be compatible with this index */ - ){ - pScan->pOrigWC = pWC; - pScan->pWC = pWC; - pScan->pIdxExpr = 0; - pScan->idxaff = 0; - pScan->zCollName = 0; - pScan->opMask = opMask; - pScan->k = 0; - pScan->aiCur[0] = iCur; - pScan->nEquiv = 1; - pScan->iEquiv = 1; - if (pIdx) { - int j = iColumn; - iColumn = pIdx->aiColumn[j]; - if (iColumn==XN_EXPR) { - pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; - pScan->zCollName = pIdx->azColl[j]; - pScan->aiColumn[0] = XN_EXPR; - return whereScanInitIndexExpr(pScan); - } else if (iColumn==pIdx->pTable->iPKey) { - iColumn = XN_ROWID; - } else if (iColumn>=0) { - pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; - pScan->zCollName = pIdx->azColl[j]; - } - } else if (iColumn==XN_EXPR) { - return 0; - } - pScan->aiColumn[0] = iColumn; - return whereScanNext(pScan); -} - -/* -** Search for a term in the WHERE clause that is of the form "X " -** where X is a reference to the iColumn of table iCur or of index pIdx -** if pIdx!=0 and is one of the WO_xx operator codes specified by -** the op parameter. Return a pointer to the term. Return 0 if not found. -** -** If pIdx!=0 then it must be one of the indexes of table iCur. -** Search for terms matching the iColumn-th column of pIdx -** rather than the iColumn-th column of table iCur. -** -** The term returned might by Y= if there is another constraint in -** the WHERE clause that specifies that X=Y. Any such constraints will be -** identified by the WO_EQUIV bit in the pTerm->eOperator field. The -** aiCur[]/iaColumn[] arrays hold X and all its equivalents. There are 11 -** slots in aiCur[]/aiColumn[] so that means we can look for X plus up to 10 -** other equivalent values. Hence a search for X will return if X=A1 -** and A1=A2 and A2=A3 and ... and A9=A10 and A10=. -** -** If there are multiple terms in the WHERE clause of the form "X " -** then try for the one with no dependencies on - in other words where -** is a constant expression of some kind. Only return entries of -** the form "X Y" where Y is a column in another table if no terms of -** the form "X " exist. If no terms with a constant RHS -** exist, try to return a term that does not use WO_EQUIV. -*/ -WhereTerm *sqlite3WhereFindTerm( - WhereClause *pWC, /* The WHERE clause to be searched */ - int iCur, /* Cursor number of LHS */ - int iColumn, /* Column number of LHS */ - Bitmask notReady, /* RHS must not overlap with this mask */ - u32 op, /* Mask of WO_xx values describing operator */ - Index *pIdx /* Must be compatible with this index, if not NULL */ - ){ - WhereTerm *pResult = 0; - WhereTerm *p; - WhereScan scan; - - p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx); - op &= WO_EQ|WO_IS; - while (p) { - if ((p->prereqRight & notReady)==0) { - if (p->prereqRight==0 && (p->eOperator&op)!=0) { - testcase( p->eOperator & WO_IS ); - return p; - } - if (pResult==0) pResult = p; - } - p = whereScanNext(&scan); - } - return pResult; -} - -/* -** This function searches pList for an entry that matches the iCol-th column -** of index pIdx. -** -** If such an expression is found, its index in pList->a[] is returned. If -** no expression is found, -1 is returned. -*/ -static int findIndexCol( - Parse *pParse, /* Parse context */ - ExprList *pList, /* Expression list to search */ - int iBase, /* Cursor for table associated with pIdx */ - Index *pIdx, /* Index to match column of */ - int iCol /* Column of index to match */ - ){ - int i; - const char *zColl = pIdx->azColl[iCol]; - - for (i=0; inExpr; i++) { - Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr); - if (p->op==TK_COLUMN - && p->iColumn==pIdx->aiColumn[iCol] - && p->iTable==iBase - ) { - CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr); - if (0==sqlite3StrICmp(pColl->zName, zColl)) { - return i; - } - } - } - - return -1; -} - -/* -** Return TRUE if the iCol-th column of index pIdx is NOT NULL -*/ -static int indexColumnNotNull(Index *pIdx, int iCol){ - int j; - assert( pIdx!=0 ); - assert( iCol>=0 && iColnColumn ); - j = pIdx->aiColumn[iCol]; - if (j>=0) { - return pIdx->pTable->aCol[j].notNull; - } else if (j==(-1)) { - return 1; - } else { - assert( j==(-2)); - return 0; /* Assume an indexed expression can always yield a NULL */ - - } -} - -/* -** Return true if the DISTINCT expression-list passed as the third argument -** is redundant. -** -** A DISTINCT list is redundant if any subset of the columns in the -** DISTINCT list are collectively unique and individually non-null. -*/ -static int isDistinctRedundant( - Parse *pParse, /* Parsing context */ - SrcList *pTabList, /* The FROM clause */ - WhereClause *pWC, /* The WHERE clause */ - ExprList *pDistinct /* The result set that needs to be DISTINCT */ - ){ - Table *pTab; - Index *pIdx; - int i; - int iBase; - - /* If there is more than one table or sub-select in the FROM clause of - ** this query, then it will not be possible to show that the DISTINCT - ** clause is redundant. */ - if (pTabList->nSrc!=1) return 0; - iBase = pTabList->a[0].iCursor; - pTab = pTabList->a[0].pTab; - - /* If any of the expressions is an IPK column on table iBase, then return - ** true. Note: The (p->iTable==iBase) part of this test may be false if the - ** current SELECT is a correlated sub-query. - */ - for (i=0; inExpr; i++) { - Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr); - if (p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0) return 1; - } - - /* Loop through all indices on the table, checking each to see if it makes - ** the DISTINCT qualifier redundant. It does so if: - ** - ** 1. The index is itself UNIQUE, and - ** - ** 2. All of the columns in the index are either part of the pDistinct - ** list, or else the WHERE clause contains a term of the form "col=X", - ** where X is a constant value. The collation sequences of the - ** comparison and select-list expressions must match those of the index. - ** - ** 3. All of those index columns for which the WHERE clause does not - ** contain a "col=X" term are subject to a NOT NULL constraint. - */ - for (pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext) { - if (!IsUniqueIndex(pIdx)) continue; - for (i=0; inKeyCol; i++) { - if (0==sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask)0, WO_EQ, pIdx)) { - if (findIndexCol(pParse, pDistinct, iBase, pIdx, i)<0) break; - if (indexColumnNotNull(pIdx, i)==0) break; - } - } - if (i==pIdx->nKeyCol) { - /* This index implies that the DISTINCT qualifier is redundant. */ - return 1; - } - } - - return 0; -} - - -/* -** Estimate the logarithm of the input value to base 2. -*/ -static LogEst estLog(LogEst N){ - return N<=10 ? 0 : sqlite3LogEst(N) - 33; -} - -/* -** Convert OP_Column opcodes to OP_Copy in previously generated code. -** -** This routine runs over generated VDBE code and translates OP_Column -** opcodes into OP_Copy when the table is being accessed via co-routine -** instead of via table lookup. -** -** If the iAutoidxCur is not zero, then any OP_Rowid instructions on -** cursor iTabCur are transformed into OP_Sequence opcode for the -** iAutoidxCur cursor, in order to generate unique rowids for the -** automatic index being generated. -*/ -static void translateColumnToCopy( - Parse *pParse, /* Parsing context */ - int iStart, /* Translate from this opcode to the end */ - int iTabCur, /* OP_Column/OP_Rowid references to this table */ - int iRegister, /* The first column is in this register */ - int iAutoidxCur /* If non-zero, cursor of autoindex being generated */ - ){ - Vdbe *v = pParse->pVdbe; - VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart); - int iEnd = sqlite3VdbeCurrentAddr(v); - if (pParse->db->mallocFailed) return; - for (; iStartp1!=iTabCur) continue; - if (pOp->opcode==OP_Column) { - pOp->opcode = OP_Copy; - pOp->p1 = pOp->p2 + iRegister; - pOp->p2 = pOp->p3; - pOp->p3 = 0; - } else if (pOp->opcode==OP_Rowid) { - if (iAutoidxCur) { - pOp->opcode = OP_Sequence; - pOp->p1 = iAutoidxCur; - } else { - pOp->opcode = OP_Null; - pOp->p1 = 0; - pOp->p3 = 0; - } - } - } -} - -/* -** Two routines for printing the content of an sqlite3_index_info -** structure. Used for testing and debugging only. If neither -** SQLITE_TEST or SQLITE_DEBUG are defined, then these routines -** are no-ops. -*/ -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED) -static void TRACE_IDX_INPUTS(sqlite3_index_info *p){ - int i; - if (!sqlite3WhereTrace) return; - for (i=0; inConstraint; i++) { - sqlite3DebugPrintf(" constraint[%d]: col=%d termid=%d op=%d usabled=%d\n", - i, - p->aConstraint[i].iColumn, - p->aConstraint[i].iTermOffset, - p->aConstraint[i].op, - p->aConstraint[i].usable); - } - for (i=0; inOrderBy; i++) { - sqlite3DebugPrintf(" orderby[%d]: col=%d desc=%d\n", - i, - p->aOrderBy[i].iColumn, - p->aOrderBy[i].desc); - } -} -static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){ - int i; - if (!sqlite3WhereTrace) return; - for (i=0; inConstraint; i++) { - sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n", - i, - p->aConstraintUsage[i].argvIndex, - p->aConstraintUsage[i].omit); - } - sqlite3DebugPrintf(" idxNum=%d\n", p->idxNum); - sqlite3DebugPrintf(" idxStr=%s\n", p->idxStr); - sqlite3DebugPrintf(" orderByConsumed=%d\n", p->orderByConsumed); - sqlite3DebugPrintf(" estimatedCost=%g\n", p->estimatedCost); - sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows); -} -#else -#define TRACE_IDX_INPUTS(A) -#define TRACE_IDX_OUTPUTS(A) -#endif - -#ifndef SQLITE_OMIT_AUTOMATIC_INDEX -/* -** Return TRUE if the WHERE clause term pTerm is of a form where it -** could be used with an index to access pSrc, assuming an appropriate -** index existed. -*/ -static int termCanDriveIndex( - WhereTerm *pTerm, /* WHERE clause term to check */ - struct SrcList_item *pSrc, /* Table we are trying to access */ - Bitmask notReady /* Tables in outer loops of the join */ - ){ - char aff; - if (pTerm->leftCursor!=pSrc->iCursor) return 0; - if ((pTerm->eOperator & (WO_EQ|WO_IS))==0) return 0; - if ((pSrc->fg.jointype & JT_LEFT) - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - && (pTerm->eOperator & WO_IS) - ) { - /* Cannot use an IS term from the WHERE clause as an index driver for - ** the RHS of a LEFT JOIN. Such a term can only be used if it is from - ** the ON clause. */ - return 0; - } - if ((pTerm->prereqRight & notReady)!=0) return 0; - if (pTerm->u.leftColumn<0) return 0; - aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; - if (!sqlite3IndexAffinityOk(pTerm->pExpr, aff)) return 0; - testcase( pTerm->pExpr->op==TK_IS ); - return 1; -} -#endif - - -#ifndef SQLITE_OMIT_AUTOMATIC_INDEX -/* -** Generate code to construct the Index object for an automatic index -** and to set up the WhereLevel object pLevel so that the code generator -** makes use of the automatic index. -*/ -static void constructAutomaticIndex( - Parse *pParse, /* The parsing context */ - WhereClause *pWC, /* The WHERE clause */ - struct SrcList_item *pSrc, /* The FROM clause term to get the next index */ - Bitmask notReady, /* Mask of cursors that are not available */ - WhereLevel *pLevel /* Write new index here */ - ){ - int nKeyCol; /* Number of columns in the constructed index */ - WhereTerm *pTerm; /* A single term of the WHERE clause */ - WhereTerm *pWCEnd; /* End of pWC->a[] */ - Index *pIdx; /* Object describing the transient index */ - Vdbe *v; /* Prepared statement under construction */ - int addrInit; /* Address of the initialization bypass jump */ - Table *pTable; /* The table being indexed */ - int addrTop; /* Top of the index fill loop */ - int regRecord; /* Register holding an index record */ - int n; /* Column counter */ - int i; /* Loop counter */ - int mxBitCol; /* Maximum column in pSrc->colUsed */ - CollSeq *pColl; /* Collating sequence to on a column */ - WhereLoop *pLoop; /* The Loop object */ - char *zNotUsed; /* Extra space on the end of pIdx */ - Bitmask idxCols; /* Bitmap of columns used for indexing */ - Bitmask extraCols; /* Bitmap of additional columns */ - u8 sentWarning = 0; /* True if a warnning has been issued */ - Expr *pPartial = 0; /* Partial Index Expression */ - int iContinue = 0; /* Jump here to skip excluded rows */ - struct SrcList_item *pTabItem; /* FROM clause term being indexed */ - int addrCounter = 0; /* Address where integer counter is initialized */ - int regBase; /* Array of registers where record is assembled */ - - /* Generate code to skip over the creation and initialization of the - ** transient index on 2nd and subsequent iterations of the loop. */ - v = pParse->pVdbe; - assert( v!=0 ); - addrInit = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - - /* Count the number of columns that will be added to the index - ** and used to match WHERE clause constraints */ - nKeyCol = 0; - pTable = pSrc->pTab; - pWCEnd = &pWC->a[pWC->nTerm]; - pLoop = pLevel->pWLoop; - idxCols = 0; - for (pTerm=pWC->a; pTermpExpr; - assert( !ExprHasProperty(pExpr, EP_FromJoin) /* prereq always non-zero */ - || pExpr->iRightJoinTable!=pSrc->iCursor /* for the right-hand */ - || pLoop->prereq!=0 ); /* table of a LEFT JOIN */ - if (pLoop->prereq==0 - && (pTerm->wtFlags & TERM_VIRTUAL)==0 - && !ExprHasProperty(pExpr, EP_FromJoin) - && sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor)) { - pPartial = sqlite3ExprAnd(pParse, pPartial, - sqlite3ExprDup(pParse->db, pExpr, 0)); - } - if (termCanDriveIndex(pTerm, pSrc, notReady)) { - int iCol = pTerm->u.leftColumn; - Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); - testcase( iCol==BMS ); - testcase( iCol==BMS-1 ); - if (!sentWarning) { - sqlite3_log(SQLITE_WARNING_AUTOINDEX, - "automatic index on %s(%s)", pTable->zName, - pTable->aCol[iCol].zName); - sentWarning = 1; - } - if ((idxCols & cMask)==0) { - if (whereLoopResize(pParse->db, pLoop, nKeyCol+1)) { - goto end_auto_index_create; - } - pLoop->aLTerm[nKeyCol++] = pTerm; - idxCols |= cMask; - } - } - } - assert( nKeyCol>0 ); - pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol; - pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED - | WHERE_AUTO_INDEX; - - /* Count the number of additional columns needed to create a - ** covering index. A "covering index" is an index that contains all - ** columns that are needed by the query. With a covering index, the - ** original table never needs to be accessed. Automatic indices must - ** be a covering index because the index will not be updated if the - ** original table changes and the index and table cannot both be used - ** if they go out of sync. - */ - extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); - mxBitCol = MIN(BMS-1,pTable->nCol); - testcase( pTable->nCol==BMS-1 ); - testcase( pTable->nCol==BMS-2 ); - for (i=0; icolUsed & MASKBIT(BMS-1)) { - nKeyCol += pTable->nCol - BMS + 1; - } - - /* Construct the Index object to describe this index */ - pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed); - if (pIdx==0) goto end_auto_index_create; - pLoop->u.btree.pIndex = pIdx; - pIdx->zName = "auto-index"; - pIdx->pTable = pTable; - n = 0; - idxCols = 0; - for (pTerm=pWC->a; pTermu.leftColumn; - Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); - testcase( iCol==BMS-1 ); - testcase( iCol==BMS ); - if ((idxCols & cMask)==0) { - Expr *pX = pTerm->pExpr; - idxCols |= cMask; - pIdx->aiColumn[n] = pTerm->u.leftColumn; - pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - pIdx->azColl[n] = pColl ? pColl->zName : sqlite3StrBINARY; - n++; - } - } - } - assert((u32)n==pLoop->u.btree.nEq ); - - /* Add additional columns needed to make the automatic index into - ** a covering index */ - for (i=0; iaiColumn[n] = i; - pIdx->azColl[n] = sqlite3StrBINARY; - n++; - } - } - if (pSrc->colUsed & MASKBIT(BMS-1)) { - for (i=BMS-1; inCol; i++) { - pIdx->aiColumn[n] = i; - pIdx->azColl[n] = sqlite3StrBINARY; - n++; - } - } - assert( n==nKeyCol ); - pIdx->aiColumn[n] = XN_ROWID; - pIdx->azColl[n] = sqlite3StrBINARY; - - /* Create the automatic index */ - assert( pLevel->iIdxCur>=0 ); - pLevel->iIdxCur = pParse->nTab++; - sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); - sqlite3VdbeSetP4KeyInfo(pParse, pIdx); - VdbeComment((v, "for %s", pTable->zName)); - - /* Fill the automatic index with content */ - pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; - if (pTabItem->fg.viaCoroutine) { - int regYield = pTabItem->regReturn; - addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); - sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); - addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); - VdbeCoverage(v); - VdbeComment((v, "next row of %s", pTabItem->pTab->zName)); - } else { - addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); - } - if (pPartial) { - iContinue = sqlite3VdbeMakeLabel(pParse); - sqlite3ExprIfFalse(pParse, pPartial, iContinue, SQLITE_JUMPIFNULL); - pLoop->wsFlags |= WHERE_PARTIALIDX; - } - regRecord = sqlite3GetTempReg(pParse); - regBase = sqlite3GenerateIndexKey( - pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0 - ); - sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); - sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); - if (pPartial) sqlite3VdbeResolveLabel(v, iContinue); - if (pTabItem->fg.viaCoroutine) { - sqlite3VdbeChangeP2(v, addrCounter, regBase+n); - testcase( pParse->db->mallocFailed ); - assert( pLevel->iIdxCur>0 ); - translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, - pTabItem->regResult, pLevel->iIdxCur); - sqlite3VdbeGoto(v, addrTop); - pTabItem->fg.viaCoroutine = 0; - } else { - sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); - } - sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); - sqlite3VdbeJumpHere(v, addrTop); - sqlite3ReleaseTempReg(pParse, regRecord); - - /* Jump here when skipping the initialization */ - sqlite3VdbeJumpHere(v, addrInit); - -end_auto_index_create: - sqlite3ExprDelete(pParse->db, pPartial); -} -#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* -** Allocate and populate an sqlite3_index_info structure. It is the -** responsibility of the caller to eventually release the structure -** by passing the pointer returned by this function to sqlite3_free(). -*/ -static sqlite3_index_info *allocateIndexInfo( - Parse *pParse, /* The parsing context */ - WhereClause *pWC, /* The WHERE clause being analyzed */ - Bitmask mUnusable, /* Ignore terms with these prereqs */ - struct SrcList_item *pSrc, /* The FROM clause term that is the vtab */ - ExprList *pOrderBy, /* The ORDER BY clause */ - u16 *pmNoOmit /* Mask of terms not to omit */ - ){ - int i, j; - int nTerm; - struct sqlite3_index_constraint *pIdxCons; - struct sqlite3_index_orderby *pIdxOrderBy; - struct sqlite3_index_constraint_usage *pUsage; - struct HiddenIndexInfo *pHidden; - WhereTerm *pTerm; - int nOrderBy; - sqlite3_index_info *pIdxInfo; - u16 mNoOmit = 0; - - /* Count the number of possible WHERE clause constraints referring - ** to this virtual table */ - for (i=nTerm=0, pTerm=pWC->a; inTerm; i++, pTerm++) { - if (pTerm->leftCursor != pSrc->iCursor) continue; - if (pTerm->prereqRight & mUnusable) continue; - assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV)); - testcase( pTerm->eOperator & WO_IN ); - testcase( pTerm->eOperator & WO_ISNULL ); - testcase( pTerm->eOperator & WO_IS ); - testcase( pTerm->eOperator & WO_ALL ); - if ((pTerm->eOperator & ~(WO_EQUIV))==0) continue; - if (pTerm->wtFlags & TERM_VNULL) continue; - assert( pTerm->u.leftColumn>=(-1)); - nTerm++; - } - - /* If the ORDER BY clause contains only columns in the current - ** virtual table then allocate space for the aOrderBy part of - ** the sqlite3_index_info structure. - */ - nOrderBy = 0; - if (pOrderBy) { - int n = pOrderBy->nExpr; - for (i=0; ia[i].pExpr; - if (pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor) break; - } - if (i==n) { - nOrderBy = n; - } - } - - /* Allocate the sqlite3_index_info structure - */ - pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) - + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm - + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden)); - if (pIdxInfo==0) { - sqlite3ErrorMsg(pParse, "out of memory"); - return 0; - } - - /* Initialize the structure. The sqlite3_index_info structure contains - ** many fields that are declared "const" to prevent xBestIndex from - ** changing them. We have to do some funky casting in order to - ** initialize those fields. - */ - pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; - pIdxCons = (struct sqlite3_index_constraint*)&pHidden[1]; - pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm]; - pUsage = (struct sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy]; - *(int*)&pIdxInfo->nConstraint = nTerm; - *(int*)&pIdxInfo->nOrderBy = nOrderBy; - *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint = pIdxCons; - *(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy; - *(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage = - pUsage; - - pHidden->pWC = pWC; - pHidden->pParse = pParse; - for (i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++) { - u16 op; - if (pTerm->leftCursor != pSrc->iCursor) continue; - if (pTerm->prereqRight & mUnusable) continue; - assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV)); - testcase( pTerm->eOperator & WO_IN ); - testcase( pTerm->eOperator & WO_IS ); - testcase( pTerm->eOperator & WO_ISNULL ); - testcase( pTerm->eOperator & WO_ALL ); - if ((pTerm->eOperator & ~(WO_EQUIV))==0) continue; - if (pTerm->wtFlags & TERM_VNULL) continue; - if ((pSrc->fg.jointype & JT_LEFT)!=0 - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - && (pTerm->eOperator & (WO_IS|WO_ISNULL)) - ) { - /* An "IS" term in the WHERE clause where the virtual table is the rhs - ** of a LEFT JOIN. Do not pass this term to the virtual table - ** implementation, as this can lead to incorrect results from SQL such - ** as: - ** - ** "LEFT JOIN vtab WHERE vtab.col IS NULL" */ - testcase( pTerm->eOperator & WO_ISNULL ); - testcase( pTerm->eOperator & WO_IS ); - continue; - } - assert( pTerm->u.leftColumn>=(-1)); - pIdxCons[j].iColumn = pTerm->u.leftColumn; - pIdxCons[j].iTermOffset = i; - op = pTerm->eOperator & WO_ALL; - if (op==WO_IN) op = WO_EQ; - if (op==WO_AUX) { - pIdxCons[j].op = pTerm->eMatchOp; - } else if (op & (WO_ISNULL|WO_IS)) { - if (op==WO_ISNULL) { - pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL; - } else { - pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS; - } - } else { - pIdxCons[j].op = (u8)op; - /* The direct assignment in the previous line is possible only because - ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The - ** following asserts verify this fact. */ - assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); - assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); - assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); - assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); - assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); - assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX)); - - if (op & (WO_LT|WO_LE|WO_GT|WO_GE) - && sqlite3ExprIsVector(pTerm->pExpr->pRight) - ) { - if (i<16) mNoOmit |= (1 << i); - if (op==WO_LT) pIdxCons[j].op = WO_LE; - if (op==WO_GT) pIdxCons[j].op = WO_GE; - } - } - - j++; - } - for (i=0; ia[i].pExpr; - pIdxOrderBy[i].iColumn = pExpr->iColumn; - pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder; - } - - *pmNoOmit = mNoOmit; - return pIdxInfo; -} - -/* -** The table object reference passed as the second argument to this function -** must represent a virtual table. This function invokes the xBestIndex() -** method of the virtual table with the sqlite3_index_info object that -** comes in as the 3rd argument to this function. -** -** If an error occurs, pParse is populated with an error message and an -** appropriate error code is returned. A return of SQLITE_CONSTRAINT from -** xBestIndex is not considered an error. SQLITE_CONSTRAINT indicates that -** the current configuration of "unusable" flags in sqlite3_index_info can -** not result in a valid plan. -** -** Whether or not an error is returned, it is the responsibility of the -** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates -** that this is required. -*/ -static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ - sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; - int rc; - - TRACE_IDX_INPUTS(p); - rc = pVtab->pModule->xBestIndex(pVtab, p); - TRACE_IDX_OUTPUTS(p); - - if (rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT) { - if (rc==SQLITE_NOMEM) { - sqlite3OomFault(pParse->db); - } else if (!pVtab->zErrMsg) { - sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc)); - } else { - sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg); - } - } - sqlite3_free(pVtab->zErrMsg); - pVtab->zErrMsg = 0; - return rc; -} -#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ - -#ifdef SQLITE_ENABLE_STAT4 -/* -** Estimate the location of a particular key among all keys in an -** index. Store the results in aStat as follows: -** -** aStat[0] Est. number of rows less than pRec -** aStat[1] Est. number of rows equal to pRec -** -** Return the index of the sample that is the smallest sample that -** is greater than or equal to pRec. Note that this index is not an index -** into the aSample[] array - it is an index into a virtual set of samples -** based on the contents of aSample[] and the number of fields in record -** pRec. -*/ -static int whereKeyStats( - Parse *pParse, /* Database connection */ - Index *pIdx, /* Index to consider domain of */ - UnpackedRecord *pRec, /* Vector of values to consider */ - int roundUp, /* Round up if true. Round down if false */ - tRowcnt *aStat /* OUT: stats written here */ - ){ - IndexSample *aSample = pIdx->aSample; - int iCol; /* Index of required stats in anEq[] etc. */ - int i; /* Index of first sample >= pRec */ - int iSample; /* Smallest sample larger than or equal to pRec */ - int iMin = 0; /* Smallest sample not yet tested */ - int iTest; /* Next sample to test */ - int res; /* Result of comparison operation */ - int nField; /* Number of fields in pRec */ - tRowcnt iLower = 0; /* anLt[] + anEq[] of largest sample pRec is > */ - -#ifndef SQLITE_DEBUG - UNUSED_PARAMETER( pParse ); -#endif - assert( pRec!=0 ); - assert( pIdx->nSample>0 ); - assert( pRec->nField>0 && pRec->nField<=pIdx->nSampleCol ); - - /* Do a binary search to find the first sample greater than or equal - ** to pRec. If pRec contains a single field, the set of samples to search - ** is simply the aSample[] array. If the samples in aSample[] contain more - ** than one fields, all fields following the first are ignored. - ** - ** If pRec contains N fields, where N is more than one, then as well as the - ** samples in aSample[] (truncated to N fields), the search also has to - ** consider prefixes of those samples. For example, if the set of samples - ** in aSample is: - ** - ** aSample[0] = (a, 5) - ** aSample[1] = (a, 10) - ** aSample[2] = (b, 5) - ** aSample[3] = (c, 100) - ** aSample[4] = (c, 105) - ** - ** Then the search space should ideally be the samples above and the - ** unique prefixes [a], [b] and [c]. But since that is hard to organize, - ** the code actually searches this set: - ** - ** 0: (a) - ** 1: (a, 5) - ** 2: (a, 10) - ** 3: (a, 10) - ** 4: (b) - ** 5: (b, 5) - ** 6: (c) - ** 7: (c, 100) - ** 8: (c, 105) - ** 9: (c, 105) - ** - ** For each sample in the aSample[] array, N samples are present in the - ** effective sample array. In the above, samples 0 and 1 are based on - ** sample aSample[0]. Samples 2 and 3 on aSample[1] etc. - ** - ** Often, sample i of each block of N effective samples has (i+1) fields. - ** Except, each sample may be extended to ensure that it is greater than or - ** equal to the previous sample in the array. For example, in the above, - ** sample 2 is the first sample of a block of N samples, so at first it - ** appears that it should be 1 field in size. However, that would make it - ** smaller than sample 1, so the binary search would not work. As a result, - ** it is extended to two fields. The duplicates that this creates do not - ** cause any problems. - */ - nField = pRec->nField; - iCol = 0; - iSample = pIdx->nSample * nField; - do{ - int iSamp; /* Index in aSample[] of test sample */ - int n; /* Number of fields in test sample */ - - iTest = (iMin+iSample)/2; - iSamp = iTest / nField; - if (iSamp>0) { - /* The proposed effective sample is a prefix of sample aSample[iSamp]. - ** Specifically, the shortest prefix of at least (1 + iTest%nField) - ** fields that is greater than the previous effective sample. */ - for (n=(iTest % nField) + 1; nnField = n; - res = sqlite3VdbeRecordCompare(aSample[iSamp].n, aSample[iSamp].p, pRec); - if (res<0) { - iLower = aSample[iSamp].anLt[n-1] + aSample[iSamp].anEq[n-1]; - iMin = iTest+1; - } else if (res==0 && ndb->mallocFailed==0) { - if (res==0) { - /* If (res==0) is true, then pRec must be equal to sample i. */ - assert( inSample ); - assert( iCol==nField-1 ); - pRec->nField = nField; - assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec) - || pParse->db->mallocFailed - ); - } else { - /* Unless i==pIdx->nSample, indicating that pRec is larger than - ** all samples in the aSample[] array, pRec must be smaller than the - ** (iCol+1) field prefix of sample i. */ - assert( i<=pIdx->nSample && i>=0 ); - pRec->nField = iCol+1; - assert( i==pIdx->nSample - || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0 - || pParse->db->mallocFailed ); - - /* if i==0 and iCol==0, then record pRec is smaller than all samples - ** in the aSample[] array. Otherwise, if (iCol>0) then pRec must - ** be greater than or equal to the (iCol) field prefix of sample i. - ** If (i>0), then pRec must also be greater than sample (i-1). */ - if (iCol>0) { - pRec->nField = iCol; - assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0 - || pParse->db->mallocFailed ); - } - if (i>0) { - pRec->nField = nField; - assert( sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 - || pParse->db->mallocFailed ); - } - } - } -#endif /* ifdef SQLITE_DEBUG */ - - if (res==0) { - /* Record pRec is equal to sample i */ - assert( iCol==nField-1 ); - aStat[0] = aSample[i].anLt[iCol]; - aStat[1] = aSample[i].anEq[iCol]; - } else { - /* At this point, the (iCol+1) field prefix of aSample[i] is the first - ** sample that is greater than pRec. Or, if i==pIdx->nSample then pRec - ** is larger than all samples in the array. */ - tRowcnt iUpper, iGap; - if (i>=pIdx->nSample) { - iUpper = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]); - } else { - iUpper = aSample[i].anLt[iCol]; - } - - if (iLower>=iUpper) { - iGap = 0; - } else { - iGap = iUpper - iLower; - } - if (roundUp) { - iGap = (iGap*2)/3; - } else { - iGap = iGap/3; - } - aStat[0] = iLower + iGap; - aStat[1] = pIdx->aAvgEq[nField-1]; - } - - /* Restore the pRec->nField value before returning. */ - pRec->nField = nField; - return i; -} -#endif /* SQLITE_ENABLE_STAT4 */ - -/* -** If it is not NULL, pTerm is a term that provides an upper or lower -** bound on a range scan. Without considering pTerm, it is estimated -** that the scan will visit nNew rows. This function returns the number -** estimated to be visited after taking pTerm into account. -** -** If the user explicitly specified a likelihood() value for this term, -** then the return value is the likelihood multiplied by the number of -** input rows. Otherwise, this function assumes that an "IS NOT NULL" term -** has a likelihood of 0.50, and any other term a likelihood of 0.25. -*/ -static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){ - LogEst nRet = nNew; - if (pTerm) { - if (pTerm->truthProb<=0) { - nRet += pTerm->truthProb; - } else if ((pTerm->wtFlags & TERM_VNULL)==0) { - nRet -= 20; assert( 20==sqlite3LogEst(4)); - } - } - return nRet; -} - - -#ifdef SQLITE_ENABLE_STAT4 -/* -** Return the affinity for a single column of an index. -*/ -char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){ - assert( iCol>=0 && iColnColumn ); - if (!pIdx->zColAff) { - if (sqlite3IndexAffinityStr(db, pIdx)==0) return SQLITE_AFF_BLOB; - } - assert( pIdx->zColAff[iCol]!=0 ); - return pIdx->zColAff[iCol]; -} -#endif - - -#ifdef SQLITE_ENABLE_STAT4 -/* -** This function is called to estimate the number of rows visited by a -** range-scan on a skip-scan index. For example: -** -** CREATE INDEX i1 ON t1(a, b, c); -** SELECT * FROM t1 WHERE a=? AND c BETWEEN ? AND ?; -** -** Value pLoop->nOut is currently set to the estimated number of rows -** visited for scanning (a=? AND b=?). This function reduces that estimate -** by some factor to account for the (c BETWEEN ? AND ?) expression based -** on the stat4 data for the index. this scan will be peformed multiple -** times (once for each (a,b) combination that matches a=?) is dealt with -** by the caller. -** -** It does this by scanning through all stat4 samples, comparing values -** extracted from pLower and pUpper with the corresponding column in each -** sample. If L and U are the number of samples found to be less than or -** equal to the values extracted from pLower and pUpper respectively, and -** N is the total number of samples, the pLoop->nOut value is adjusted -** as follows: -** -** nOut = nOut * ( min(U - L, 1) / N ) -** -** If pLower is NULL, or a value cannot be extracted from the term, L is -** set to zero. If pUpper is NULL, or a value cannot be extracted from it, -** U is set to N. -** -** Normally, this function sets *pbDone to 1 before returning. However, -** if no value can be extracted from either pLower or pUpper (and so the -** estimate of the number of rows delivered remains unchanged), *pbDone -** is left as is. -** -** If an error occurs, an SQLite error code is returned. Otherwise, -** SQLITE_OK. -*/ -static int whereRangeSkipScanEst( - Parse *pParse, /* Parsing & code generating context */ - WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */ - WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ - WhereLoop *pLoop, /* Update the .nOut value of this loop */ - int *pbDone /* Set to true if at least one expr. value extracted */ - ){ - Index *p = pLoop->u.btree.pIndex; - int nEq = pLoop->u.btree.nEq; - sqlite3 *db = pParse->db; - int nLower = -1; - int nUpper = p->nSample+1; - int rc = SQLITE_OK; - u8 aff = sqlite3IndexColumnAffinity(db, p, nEq); - CollSeq *pColl; - - sqlite3_value *p1 = 0; /* Value extracted from pLower */ - sqlite3_value *p2 = 0; /* Value extracted from pUpper */ - sqlite3_value *pVal = 0; /* Value extracted from record */ - - pColl = sqlite3LocateCollSeq(pParse, p->azColl[nEq]); - if (pLower) { - rc = sqlite3Stat4ValueFromExpr(pParse, pLower->pExpr->pRight, aff, &p1); - nLower = 0; - } - if (pUpper && rc==SQLITE_OK) { - rc = sqlite3Stat4ValueFromExpr(pParse, pUpper->pExpr->pRight, aff, &p2); - nUpper = p2 ? 0 : p->nSample; - } - - if (p1 || p2) { - int i; - int nDiff; - for (i=0; rc==SQLITE_OK && inSample; i++) { - rc = sqlite3Stat4Column(db, p->aSample[i].p, p->aSample[i].n, nEq, &pVal); - if (rc==SQLITE_OK && p1) { - int res = sqlite3MemCompare(p1, pVal, pColl); - if (res>=0) nLower++; - } - if (rc==SQLITE_OK && p2) { - int res = sqlite3MemCompare(p2, pVal, pColl); - if (res>=0) nUpper++; - } - } - nDiff = (nUpper - nLower); - if (nDiff<=0) nDiff = 1; - - /* If there is both an upper and lower bound specified, and the - ** comparisons indicate that they are close together, use the fallback - ** method (assume that the scan visits 1/64 of the rows) for estimating - ** the number of rows visited. Otherwise, estimate the number of rows - ** using the method described in the header comment for this function. */ - if (nDiff!=1 || pUpper==0 || pLower==0) { - int nAdjust = (sqlite3LogEst(p->nSample) - sqlite3LogEst(nDiff)); - pLoop->nOut -= nAdjust; - *pbDone = 1; - WHERETRACE(0x10, ("range skip-scan regions: %u..%u adjust=%d est=%d\n", - nLower, nUpper, nAdjust* -1, pLoop->nOut)); - } - - } else { - assert( *pbDone==0 ); - } - - sqlite3ValueFree(p1); - sqlite3ValueFree(p2); - sqlite3ValueFree(pVal); - - return rc; -} -#endif /* SQLITE_ENABLE_STAT4 */ - -/* -** This function is used to estimate the number of rows that will be visited -** by scanning an index for a range of values. The range may have an upper -** bound, a lower bound, or both. The WHERE clause terms that set the upper -** and lower bounds are represented by pLower and pUpper respectively. For -** example, assuming that index p is on t1(a): -** -** ... FROM t1 WHERE a > ? AND a < ? ... -** |_____| |_____| -** | | -** pLower pUpper -** -** If either of the upper or lower bound is not present, then NULL is passed in -** place of the corresponding WhereTerm. -** -** The value in (pBuilder->pNew->u.btree.nEq) is the number of the index -** column subject to the range constraint. Or, equivalently, the number of -** equality constraints optimized by the proposed index scan. For example, -** assuming index p is on t1(a, b), and the SQL query is: -** -** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ... -** -** then nEq is set to 1 (as the range restricted column, b, is the second -** left-most column of the index). Or, if the query is: -** -** ... FROM t1 WHERE a > ? AND a < ? ... -** -** then nEq is set to 0. -** -** When this function is called, *pnOut is set to the sqlite3LogEst() of the -** number of rows that the index scan is expected to visit without -** considering the range constraints. If nEq is 0, then *pnOut is the number of -** rows in the index. Assuming no error occurs, *pnOut is adjusted (reduced) -** to account for the range constraints pLower and pUpper. -** -** In the absence of sqlite_stat4 ANALYZE data, or if such data cannot be -** used, a single range inequality reduces the search space by a factor of 4. -** and a pair of constraints (x>? AND x123" Might be NULL */ - WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ - WhereLoop *pLoop /* Modify the .nOut and maybe .rRun fields */ - ){ - int rc = SQLITE_OK; - int nOut = pLoop->nOut; - LogEst nNew; - -#ifdef SQLITE_ENABLE_STAT4 - Index *p = pLoop->u.btree.pIndex; - int nEq = pLoop->u.btree.nEq; - - if (p->nSample>0 && ALWAYS(nEqnSampleCol) - && OptimizationEnabled(pParse->db, SQLITE_Stat4) - ) { - if (nEq==pBuilder->nRecValid) { - UnpackedRecord *pRec = pBuilder->pRec; - tRowcnt a[2]; - int nBtm = pLoop->u.btree.nBtm; - int nTop = pLoop->u.btree.nTop; - - /* Variable iLower will be set to the estimate of the number of rows in - ** the index that are less than the lower bound of the range query. The - ** lower bound being the concatenation of $P and $L, where $P is the - ** key-prefix formed by the nEq values matched against the nEq left-most - ** columns of the index, and $L is the value in pLower. - ** - ** Or, if pLower is NULL or $L cannot be extracted from it (because it - ** is not a simple variable or literal value), the lower bound of the - ** range is $P. Due to a quirk in the way whereKeyStats() works, even - ** if $L is available, whereKeyStats() is called for both ($P) and - ** ($P:$L) and the larger of the two returned values is used. - ** - ** Similarly, iUpper is to be set to the estimate of the number of rows - ** less than the upper bound of the range query. Where the upper bound - ** is either ($P) or ($P:$U). Again, even if $U is available, both values - ** of iUpper are requested of whereKeyStats() and the smaller used. - ** - ** The number of rows between the two bounds is then just iUpper-iLower. - */ - tRowcnt iLower; /* Rows less than the lower bound */ - tRowcnt iUpper; /* Rows less than the upper bound */ - int iLwrIdx = -2; /* aSample[] for the lower bound */ - int iUprIdx = -1; /* aSample[] for the upper bound */ - - if (pRec) { - testcase( pRec->nField!=pBuilder->nRecValid ); - pRec->nField = pBuilder->nRecValid; - } - /* Determine iLower and iUpper using ($P) only. */ - if (nEq==0) { - iLower = 0; - iUpper = p->nRowEst0; - } else { - /* Note: this call could be optimized away - since the same values must - ** have been requested when testing key $P in whereEqualScanEst(). */ - whereKeyStats(pParse, p, pRec, 0, a); - iLower = a[0]; - iUpper = a[0] + a[1]; - } - - assert( pLower==0 || (pLower->eOperator & (WO_GT|WO_GE))!=0 ); - assert( pUpper==0 || (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); - assert( p->aSortOrder!=0 ); - if (p->aSortOrder[nEq]) { - /* The roles of pLower and pUpper are swapped for a DESC index */ - SWAP(WhereTerm*, pLower, pUpper); - SWAP(int, nBtm, nTop); - } - - /* If possible, improve on the iLower estimate using ($P:$L). */ - if (pLower) { - int n; /* Values extracted from pExpr */ - Expr *pExpr = pLower->pExpr->pRight; - rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nBtm, nEq, &n); - if (rc==SQLITE_OK && n) { - tRowcnt iNew; - u16 mask = WO_GT|WO_LE; - if (sqlite3ExprVectorSize(pExpr)>n) mask = (WO_LE|WO_LT); - iLwrIdx = whereKeyStats(pParse, p, pRec, 0, a); - iNew = a[0] + ((pLower->eOperator & mask) ? a[1] : 0); - if (iNew>iLower) iLower = iNew; - nOut--; - pLower = 0; - } - } - - /* If possible, improve on the iUpper estimate using ($P:$U). */ - if (pUpper) { - int n; /* Values extracted from pExpr */ - Expr *pExpr = pUpper->pExpr->pRight; - rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nTop, nEq, &n); - if (rc==SQLITE_OK && n) { - tRowcnt iNew; - u16 mask = WO_GT|WO_LE; - if (sqlite3ExprVectorSize(pExpr)>n) mask = (WO_LE|WO_LT); - iUprIdx = whereKeyStats(pParse, p, pRec, 1, a); - iNew = a[0] + ((pUpper->eOperator & mask) ? a[1] : 0); - if (iNewpRec = pRec; - if (rc==SQLITE_OK) { - if (iUpper>iLower) { - nNew = sqlite3LogEst(iUpper - iLower); - /* TUNING: If both iUpper and iLower are derived from the same - ** sample, then assume they are 4x more selective. This brings - ** the estimated selectivity more in line with what it would be - ** if estimated without the use of STAT4 tables. */ - if (iLwrIdx==iUprIdx) nNew -= 20; assert( 20==sqlite3LogEst(4)); - } else { - nNew = 10; assert( 10==sqlite3LogEst(2)); - } - if (nNewwtFlags & TERM_VNULL)==0 ); - nNew = whereRangeAdjust(pLower, nOut); - nNew = whereRangeAdjust(pUpper, nNew); - - /* TUNING: If there is both an upper and lower limit and neither limit - ** has an application-defined likelihood(), assume the range is - ** reduced by an additional 75%. This means that, by default, an open-ended - ** range query (e.g. col > ?) is assumed to match 1/4 of the rows in the - ** index. While a closed range (e.g. col BETWEEN ? AND ?) is estimated to - ** match 1/64 of the index. */ - if (pLower && pLower->truthProb>0 && pUpper && pUpper->truthProb>0) { - nNew -= 20; - } - - nOut -= (pLower!=0) + (pUpper!=0); - if (nNew<10) nNew = 10; - if (nNewnOut>nOut) { - WHERETRACE(0x10,("Range scan lowers nOut from %d to %d\n", - pLoop->nOut, nOut)); - } -#endif - pLoop->nOut = (LogEst)nOut; - return rc; -} - -#ifdef SQLITE_ENABLE_STAT4 -/* -** Estimate the number of rows that will be returned based on -** an equality constraint x=VALUE and where that VALUE occurs in -** the histogram data. This only works when x is the left-most -** column of an index and sqlite_stat4 histogram data is available -** for that index. When pExpr==NULL that means the constraint is -** "x IS NULL" instead of "x=VALUE". -** -** Write the estimated row count into *pnRow and return SQLITE_OK. -** If unable to make an estimate, leave *pnRow unchanged and return -** non-zero. -** -** This routine can fail if it is unable to load a collating sequence -** required for string comparison, or if unable to allocate memory -** for a UTF conversion required for comparison. The error is stored -** in the pParse structure. -*/ -static int whereEqualScanEst( - Parse *pParse, /* Parsing & code generating context */ - WhereLoopBuilder *pBuilder, - Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ - tRowcnt *pnRow /* Write the revised row estimate here */ - ){ - Index *p = pBuilder->pNew->u.btree.pIndex; - int nEq = pBuilder->pNew->u.btree.nEq; - UnpackedRecord *pRec = pBuilder->pRec; - int rc; /* Subfunction return code */ - tRowcnt a[2]; /* Statistics */ - int bOk; - - assert( nEq>=1 ); - assert( nEq<=p->nColumn ); - assert( p->aSample!=0 ); - assert( p->nSample>0 ); - assert( pBuilder->nRecValidnRecValid<(nEq-1)) { - return SQLITE_NOTFOUND; - } - - /* This is an optimization only. The call to sqlite3Stat4ProbeSetValue() - ** below would return the same value. */ - if (nEq>=p->nColumn) { - *pnRow = 1; - return SQLITE_OK; - } - - rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, 1, nEq-1, &bOk); - pBuilder->pRec = pRec; - if (rc!=SQLITE_OK) return rc; - if (bOk==0) return SQLITE_NOTFOUND; - pBuilder->nRecValid = nEq; - - whereKeyStats(pParse, p, pRec, 0, a); - WHERETRACE(0x10,("equality scan regions %s(%d): %d\n", - p->zName, nEq-1, (int)a[1])); - *pnRow = a[1]; - - return rc; -} -#endif /* SQLITE_ENABLE_STAT4 */ - -#ifdef SQLITE_ENABLE_STAT4 -/* -** Estimate the number of rows that will be returned based on -** an IN constraint where the right-hand side of the IN operator -** is a list of values. Example: -** -** WHERE x IN (1,2,3,4) -** -** Write the estimated row count into *pnRow and return SQLITE_OK. -** If unable to make an estimate, leave *pnRow unchanged and return -** non-zero. -** -** This routine can fail if it is unable to load a collating sequence -** required for string comparison, or if unable to allocate memory -** for a UTF conversion required for comparison. The error is stored -** in the pParse structure. -*/ -static int whereInScanEst( - Parse *pParse, /* Parsing & code generating context */ - WhereLoopBuilder *pBuilder, - ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ - tRowcnt *pnRow /* Write the revised row estimate here */ - ){ - Index *p = pBuilder->pNew->u.btree.pIndex; - i64 nRow0 = sqlite3LogEstToInt(p->aiRowLogEst[0]); - int nRecValid = pBuilder->nRecValid; - int rc = SQLITE_OK; /* Subfunction return code */ - tRowcnt nEst; /* Number of rows for a single term */ - tRowcnt nRowEst = 0; /* New estimate of the number of rows */ - int i; /* Loop counter */ - - assert( p->aSample!=0 ); - for (i=0; rc==SQLITE_OK && inExpr; i++) { - nEst = nRow0; - rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst); - nRowEst += nEst; - pBuilder->nRecValid = nRecValid; - } - - if (rc==SQLITE_OK) { - if (nRowEst > nRow0) nRowEst = nRow0; - *pnRow = nRowEst; - WHERETRACE(0x10,("IN row estimate: est=%d\n", nRowEst)); - } - assert( pBuilder->nRecValid==nRecValid ); - return rc; -} -#endif /* SQLITE_ENABLE_STAT4 */ - - -#ifdef WHERETRACE_ENABLED -/* -** Print the content of a WhereTerm object -*/ -static void whereTermPrint(WhereTerm *pTerm, int iTerm){ - if (pTerm==0) { - sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm); - } else { - char zType[4]; - char zLeft[50]; - memcpy(zType, "...", 4); - if (pTerm->wtFlags & TERM_VIRTUAL) zType[0] = 'V'; - if (pTerm->eOperator & WO_EQUIV) zType[1] = 'E'; - if (ExprHasProperty(pTerm->pExpr, EP_FromJoin)) zType[2] = 'L'; - if (pTerm->eOperator & WO_SINGLE) { - sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}", - pTerm->leftCursor, pTerm->u.leftColumn); - } else if ((pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0) { - sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%lld", - pTerm->u.pOrInfo->indexable); - } else { - sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor); - } - sqlite3DebugPrintf( - "TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x", - iTerm, pTerm, zType, zLeft, pTerm->truthProb, - pTerm->eOperator, pTerm->wtFlags); - if (pTerm->iField) { - sqlite3DebugPrintf(" iField=%d\n", pTerm->iField); - } else { - sqlite3DebugPrintf("\n"); - } - sqlite3TreeViewExpr(0, pTerm->pExpr, 0); - } -} -#endif - -#ifdef WHERETRACE_ENABLED -/* -** Show the complete content of a WhereClause -*/ -void sqlite3WhereClausePrint(WhereClause *pWC){ - int i; - for (i=0; inTerm; i++) { - whereTermPrint(&pWC->a[i], i); - } -} -#endif - -#ifdef WHERETRACE_ENABLED -/* -** Print a WhereLoop object for debugging purposes -*/ -static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){ - WhereInfo *pWInfo = pWC->pWInfo; - int nb = 1+(pWInfo->pTabList->nSrc+3)/4; - struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab; - Table *pTab = pItem->pTab; - Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; - sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, - p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); - sqlite3DebugPrintf(" %12s", - pItem->zAlias ? pItem->zAlias : pTab->zName); - if ((p->wsFlags & WHERE_VIRTUALTABLE)==0) { - const char *zName; - if (p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0) { - if (strncmp(zName, "sqlite_autoindex_", 17)==0) { - int i = sqlite3Strlen30(zName) - 1; - while (zName[i]!='_') i--; - zName += i; - } - sqlite3DebugPrintf(".%-16s %2d", zName, p->u.btree.nEq); - } else { - sqlite3DebugPrintf("%20s",""); - } - } else { - char *z; - if (p->u.vtab.idxStr) { - z = sqlite3_mprintf("(%d,\"%s\",%x)", - p->u.vtab.idxNum, p->u.vtab.idxStr, p->u.vtab.omitMask); - } else { - z = sqlite3_mprintf("(%d,%x)", p->u.vtab.idxNum, p->u.vtab.omitMask); - } - sqlite3DebugPrintf(" %-19s", z); - sqlite3_free(z); - } - if (p->wsFlags & WHERE_SKIPSCAN) { - sqlite3DebugPrintf(" f %05x %d-%d", p->wsFlags, p->nLTerm,p->nSkip); - } else { - sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm); - } - sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); - if (p->nLTerm && (sqlite3WhereTrace & 0x100)!=0) { - int i; - for (i=0; inLTerm; i++) { - whereTermPrint(p->aLTerm[i], i); - } - } -} -#endif - -/* -** Convert bulk memory into a valid WhereLoop that can be passed -** to whereLoopClear harmlessly. -*/ -static void whereLoopInit(WhereLoop *p){ - p->aLTerm = p->aLTermSpace; - p->nLTerm = 0; - p->nLSlot = ArraySize(p->aLTermSpace); - p->wsFlags = 0; -} - -/* -** Clear the WhereLoop.u union. Leave WhereLoop.pLTerm intact. -*/ -static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){ - if (p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_AUTO_INDEX)) { - if ((p->wsFlags & WHERE_VIRTUALTABLE)!=0 && p->u.vtab.needFree) { - sqlite3_free(p->u.vtab.idxStr); - p->u.vtab.needFree = 0; - p->u.vtab.idxStr = 0; - } else if ((p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0) { - sqlite3DbFree(db, p->u.btree.pIndex->zColAff); - sqlite3DbFreeNN(db, p->u.btree.pIndex); - p->u.btree.pIndex = 0; - } - } -} - -/* -** Deallocate internal memory used by a WhereLoop object -*/ -static void whereLoopClear(sqlite3 *db, WhereLoop *p){ - if (p->aLTerm!=p->aLTermSpace) sqlite3DbFreeNN(db, p->aLTerm); - whereLoopClearUnion(db, p); - whereLoopInit(p); -} - -/* -** Increase the memory allocation for pLoop->aLTerm[] to be at least n. -*/ -static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){ - WhereTerm **paNew; - if (p->nLSlot>=n) return SQLITE_OK; - n = (n+7)&~7; - paNew = sqlite3DbMallocRawNN(db, sizeof(p->aLTerm[0])*n); - if (paNew==0) return SQLITE_NOMEM_BKPT; - memcpy(paNew, p->aLTerm, sizeof(p->aLTerm[0])*p->nLSlot); - if (p->aLTerm!=p->aLTermSpace) sqlite3DbFreeNN(db, p->aLTerm); - p->aLTerm = paNew; - p->nLSlot = n; - return SQLITE_OK; -} - -/* -** Transfer content from the second pLoop into the first. -*/ -static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ - whereLoopClearUnion(db, pTo); - if (whereLoopResize(db, pTo, pFrom->nLTerm)) { - memset(&pTo->u, 0, sizeof(pTo->u)); - return SQLITE_NOMEM_BKPT; - } - memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ); - memcpy(pTo->aLTerm, pFrom->aLTerm, pTo->nLTerm*sizeof(pTo->aLTerm[0])); - if (pFrom->wsFlags & WHERE_VIRTUALTABLE) { - pFrom->u.vtab.needFree = 0; - } else if ((pFrom->wsFlags & WHERE_AUTO_INDEX)!=0) { - pFrom->u.btree.pIndex = 0; - } - return SQLITE_OK; -} - -/* -** Delete a WhereLoop object -*/ -static void whereLoopDelete(sqlite3 *db, WhereLoop *p){ - whereLoopClear(db, p); - sqlite3DbFreeNN(db, p); -} - -/* -** Free a WhereInfo structure -*/ -static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ - int i; - assert( pWInfo!=0 ); - for (i=0; inLevel; i++) { - WhereLevel *pLevel = &pWInfo->a[i]; - if (pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE)) { - sqlite3DbFree(db, pLevel->u.in.aInLoop); - } - } - sqlite3WhereClauseClear(&pWInfo->sWC); - while (pWInfo->pLoops) { - WhereLoop *p = pWInfo->pLoops; - pWInfo->pLoops = p->pNextLoop; - whereLoopDelete(db, p); - } - sqlite3DbFreeNN(db, pWInfo); -} - -/* -** Return TRUE if all of the following are true: -** -** (1) X has the same or lower cost that Y -** (2) X uses fewer WHERE clause terms than Y -** (3) Every WHERE clause term used by X is also used by Y -** (4) X skips at least as many columns as Y -** (5) If X is a covering index, than Y is too -** -** Conditions (2) and (3) mean that X is a "proper subset" of Y. -** If X is a proper subset of Y then Y is a better choice and ought -** to have a lower cost. This routine returns TRUE when that cost -** relationship is inverted and needs to be adjusted. Constraint (4) -** was added because if X uses skip-scan less than Y it still might -** deserve a lower cost even if it is a proper subset of Y. Constraint (5) -** was added because a covering index probably deserves to have a lower cost -** than a non-covering index even if it is a proper subset. -*/ -static int whereLoopCheaperProperSubset( - const WhereLoop *pX, /* First WhereLoop to compare */ - const WhereLoop *pY /* Compare against this WhereLoop */ - ){ - int i, j; - if (pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip) { - return 0; /* X is not a subset of Y */ - } - if (pY->nSkip > pX->nSkip) return 0; - if (pX->rRun >= pY->rRun) { - if (pX->rRun > pY->rRun) return 0; /* X costs more than Y */ - if (pX->nOut > pY->nOut) return 0; /* X costs more than Y */ - } - for (i=pX->nLTerm-1; i>=0; i--) { - if (pX->aLTerm[i]==0) continue; - for (j=pY->nLTerm-1; j>=0; j--) { - if (pY->aLTerm[j]==pX->aLTerm[i]) break; - } - if (j<0) return 0; /* X not a subset of Y since term X[i] not used by Y */ - } - if ((pX->wsFlags&WHERE_IDX_ONLY)!=0 - && (pY->wsFlags&WHERE_IDX_ONLY)==0) { - return 0; /* Constraint (5) */ - } - return 1; /* All conditions meet */ -} - -/* -** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so -** that: -** -** (1) pTemplate costs less than any other WhereLoops that are a proper -** subset of pTemplate -** -** (2) pTemplate costs more than any other WhereLoops for which pTemplate -** is a proper subset. -** -** To say "WhereLoop X is a proper subset of Y" means that X uses fewer -** WHERE clause terms than Y and that every WHERE clause term used by X is -** also used by Y. -*/ -static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){ - if ((pTemplate->wsFlags & WHERE_INDEXED)==0) return; - for (; p; p=p->pNextLoop) { - if (p->iTab!=pTemplate->iTab) continue; - if ((p->wsFlags & WHERE_INDEXED)==0) continue; - if (whereLoopCheaperProperSubset(p, pTemplate)) { - /* Adjust pTemplate cost downward so that it is cheaper than its - ** subset p. */ - WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", - pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut-1)); - pTemplate->rRun = p->rRun; - pTemplate->nOut = p->nOut - 1; - } else if (whereLoopCheaperProperSubset(pTemplate, p)) { - /* Adjust pTemplate cost upward so that it is costlier than p since - ** pTemplate is a proper subset of p */ - WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", - pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut+1)); - pTemplate->rRun = p->rRun; - pTemplate->nOut = p->nOut + 1; - } - } -} - -/* -** Search the list of WhereLoops in *ppPrev looking for one that can be -** replaced by pTemplate. -** -** Return NULL if pTemplate does not belong on the WhereLoop list. -** In other words if pTemplate ought to be dropped from further consideration. -** -** If pX is a WhereLoop that pTemplate can replace, then return the -** link that points to pX. -** -** If pTemplate cannot replace any existing element of the list but needs -** to be added to the list as a new entry, then return a pointer to the -** tail of the list. -*/ -static WhereLoop **whereLoopFindLesser( - WhereLoop **ppPrev, - const WhereLoop *pTemplate - ){ - WhereLoop *p; - for (p=(*ppPrev); p; ppPrev=&p->pNextLoop, p=*ppPrev) { - if (p->iTab!=pTemplate->iTab || p->iSortIdx!=pTemplate->iSortIdx) { - /* If either the iTab or iSortIdx values for two WhereLoop are different - ** then those WhereLoops need to be considered separately. Neither is - ** a candidate to replace the other. */ - continue; - } - /* In the current implementation, the rSetup value is either zero - ** or the cost of building an automatic index (NlogN) and the NlogN - ** is the same for compatible WhereLoops. */ - assert( p->rSetup==0 || pTemplate->rSetup==0 - || p->rSetup==pTemplate->rSetup ); - - /* whereLoopAddBtree() always generates and inserts the automatic index - ** case first. Hence compatible candidate WhereLoops never have a larger - ** rSetup. Call this SETUP-INVARIANT */ - assert( p->rSetup>=pTemplate->rSetup ); - - /* Any loop using an appliation-defined index (or PRIMARY KEY or - ** UNIQUE constraint) with one or more == constraints is better - ** than an automatic index. Unless it is a skip-scan. */ - if ((p->wsFlags & WHERE_AUTO_INDEX)!=0 - && (pTemplate->nSkip)==0 - && (pTemplate->wsFlags & WHERE_INDEXED)!=0 - && (pTemplate->wsFlags & WHERE_COLUMN_EQ)!=0 - && (p->prereq & pTemplate->prereq)==pTemplate->prereq - ) { - break; - } - - /* If existing WhereLoop p is better than pTemplate, pTemplate can be - ** discarded. WhereLoop p is better if: - ** (1) p has no more dependencies than pTemplate, and - ** (2) p has an equal or lower cost than pTemplate - */ - if ((p->prereq & pTemplate->prereq)==p->prereq /* (1) */ - && p->rSetup<=pTemplate->rSetup /* (2a) */ - && p->rRun<=pTemplate->rRun /* (2b) */ - && p->nOut<=pTemplate->nOut /* (2c) */ - ) { - return 0; /* Discard pTemplate */ - } - - /* If pTemplate is always better than p, then cause p to be overwritten - ** with pTemplate. pTemplate is better than p if: - ** (1) pTemplate has no more dependences than p, and - ** (2) pTemplate has an equal or lower cost than p. - */ - if ((p->prereq & pTemplate->prereq)==pTemplate->prereq /* (1) */ - && p->rRun>=pTemplate->rRun /* (2a) */ - && p->nOut>=pTemplate->nOut /* (2b) */ - ) { - assert( p->rSetup>=pTemplate->rSetup ); /* SETUP-INVARIANT above */ - break; /* Cause p to be overwritten by pTemplate */ - } - } - return ppPrev; -} - -/* -** Insert or replace a WhereLoop entry using the template supplied. -** -** An existing WhereLoop entry might be overwritten if the new template -** is better and has fewer dependencies. Or the template will be ignored -** and no insert will occur if an existing WhereLoop is faster and has -** fewer dependencies than the template. Otherwise a new WhereLoop is -** added based on the template. -** -** If pBuilder->pOrSet is not NULL then we care about only the -** prerequisites and rRun and nOut costs of the N best loops. That -** information is gathered in the pBuilder->pOrSet object. This special -** processing mode is used only for OR clause processing. -** -** When accumulating multiple loops (when pBuilder->pOrSet is NULL) we -** still might overwrite similar loops with the new template if the -** new template is better. Loops may be overwritten if the following -** conditions are met: -** -** (1) They have the same iTab. -** (2) They have the same iSortIdx. -** (3) The template has same or fewer dependencies than the current loop -** (4) The template has the same or lower cost than the current loop -*/ -static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ - WhereLoop **ppPrev, *p; - WhereInfo *pWInfo = pBuilder->pWInfo; - sqlite3 *db = pWInfo->pParse->db; - int rc; - - /* Stop the search once we hit the query planner search limit */ - if (pBuilder->iPlanLimit==0) { - WHERETRACE(0xffffffff,("=== query planner search limit reached ===\n")); - if (pBuilder->pOrSet) pBuilder->pOrSet->n = 0; - return SQLITE_DONE; - } - pBuilder->iPlanLimit--; - - /* If pBuilder->pOrSet is defined, then only keep track of the costs - ** and prereqs. - */ - if (pBuilder->pOrSet!=0) { - if (pTemplate->nLTerm) { -#if WHERETRACE_ENABLED - u16 n = pBuilder->pOrSet->n; - int x = -#endif - whereOrInsert(pBuilder->pOrSet, pTemplate->prereq, pTemplate->rRun, - pTemplate->nOut); -#if WHERETRACE_ENABLED /* 0x8 */ - if (sqlite3WhereTrace & 0x8) { - sqlite3DebugPrintf(x?" or-%d: ":" or-X: ", n); - whereLoopPrint(pTemplate, pBuilder->pWC); - } -#endif - } - return SQLITE_OK; - } - - /* Look for an existing WhereLoop to replace with pTemplate - */ - whereLoopAdjustCost(pWInfo->pLoops, pTemplate); - ppPrev = whereLoopFindLesser(&pWInfo->pLoops, pTemplate); - - if (ppPrev==0) { - /* There already exists a WhereLoop on the list that is better - ** than pTemplate, so just ignore pTemplate */ -#if WHERETRACE_ENABLED /* 0x8 */ - if (sqlite3WhereTrace & 0x8) { - sqlite3DebugPrintf(" skip: "); - whereLoopPrint(pTemplate, pBuilder->pWC); - } -#endif - return SQLITE_OK; - } else { - p = *ppPrev; - } - - /* If we reach this point it means that either p[] should be overwritten - ** with pTemplate[] if p[] exists, or if p==NULL then allocate a new - ** WhereLoop and insert it. - */ -#if WHERETRACE_ENABLED /* 0x8 */ - if (sqlite3WhereTrace & 0x8) { - if (p!=0) { - sqlite3DebugPrintf("replace: "); - whereLoopPrint(p, pBuilder->pWC); - sqlite3DebugPrintf(" with: "); - } else { - sqlite3DebugPrintf(" add: "); - } - whereLoopPrint(pTemplate, pBuilder->pWC); - } -#endif - if (p==0) { - /* Allocate a new WhereLoop to add to the end of the list */ - *ppPrev = p = sqlite3DbMallocRawNN(db, sizeof(WhereLoop)); - if (p==0) return SQLITE_NOMEM_BKPT; - whereLoopInit(p); - p->pNextLoop = 0; - } else { - /* We will be overwriting WhereLoop p[]. But before we do, first - ** go through the rest of the list and delete any other entries besides - ** p[] that are also supplated by pTemplate */ - WhereLoop **ppTail = &p->pNextLoop; - WhereLoop *pToDel; - while (*ppTail) { - ppTail = whereLoopFindLesser(ppTail, pTemplate); - if (ppTail==0) break; - pToDel = *ppTail; - if (pToDel==0) break; - *ppTail = pToDel->pNextLoop; -#if WHERETRACE_ENABLED /* 0x8 */ - if (sqlite3WhereTrace & 0x8) { - sqlite3DebugPrintf(" delete: "); - whereLoopPrint(pToDel, pBuilder->pWC); - } -#endif - whereLoopDelete(db, pToDel); - } - } - rc = whereLoopXfer(db, p, pTemplate); - if ((p->wsFlags & WHERE_VIRTUALTABLE)==0) { - Index *pIndex = p->u.btree.pIndex; - if (pIndex && pIndex->idxType==SQLITE_IDXTYPE_IPK) { - p->u.btree.pIndex = 0; - } - } - return rc; -} - -/* -** Adjust the WhereLoop.nOut value downward to account for terms of the -** WHERE clause that reference the loop but which are not used by an -** index. -* -** For every WHERE clause term that is not used by the index -** and which has a truth probability assigned by one of the likelihood(), -** likely(), or unlikely() SQL functions, reduce the estimated number -** of output rows by the probability specified. -** -** TUNING: For every WHERE clause term that is not used by the index -** and which does not have an assigned truth probability, heuristics -** described below are used to try to estimate the truth probability. -** TODO --> Perhaps this is something that could be improved by better -** table statistics. -** -** Heuristic 1: Estimate the truth probability as 93.75%. The 93.75% -** value corresponds to -1 in LogEst notation, so this means decrement -** the WhereLoop.nOut field for every such WHERE clause term. -** -** Heuristic 2: If there exists one or more WHERE clause terms of the -** form "x==EXPR" and EXPR is not a constant 0 or 1, then make sure the -** final output row estimate is no greater than 1/4 of the total number -** of rows in the table. In other words, assume that x==EXPR will filter -** out at least 3 out of 4 rows. If EXPR is -1 or 0 or 1, then maybe the -** "x" column is boolean or else -1 or 0 or 1 is a common default value -** on the "x" column and so in that case only cap the output row estimate -** at 1/2 instead of 1/4. -*/ -static void whereLoopOutputAdjust( - WhereClause *pWC, /* The WHERE clause */ - WhereLoop *pLoop, /* The loop to adjust downward */ - LogEst nRow /* Number of rows in the entire table */ - ){ - WhereTerm *pTerm, *pX; - Bitmask notAllowed = ~(pLoop->prereq|pLoop->maskSelf); - int i, j, k; - LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */ - - assert((pLoop->wsFlags & WHERE_AUTO_INDEX)==0 ); - for (i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++) { - assert( pTerm!=0 ); - if ((pTerm->wtFlags & TERM_VIRTUAL)!=0) break; - if ((pTerm->prereqAll & pLoop->maskSelf)==0) continue; - if ((pTerm->prereqAll & notAllowed)!=0) continue; - for (j=pLoop->nLTerm-1; j>=0; j--) { - pX = pLoop->aLTerm[j]; - if (pX==0) continue; - if (pX==pTerm) break; - if (pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm) break; - } - if (j<0) { - if (pTerm->truthProb<=0) { - /* If a truth probability is specified using the likelihood() hints, - ** then use the probability provided by the application. */ - pLoop->nOut += pTerm->truthProb; - } else { - /* In the absence of explicit truth probabilities, use heuristics to - ** guess a reasonable truth probability. */ - pLoop->nOut--; - if (pTerm->eOperator&(WO_EQ|WO_IS)) { - Expr *pRight = pTerm->pExpr->pRight; - testcase( pTerm->pExpr->op==TK_IS ); - if (sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1) { - k = 10; - } else { - k = 20; - } - if (iReducenOut > nRow-iReduce) pLoop->nOut = nRow - iReduce; -} - -/* -** Term pTerm is a vector range comparison operation. The first comparison -** in the vector can be optimized using column nEq of the index. This -** function returns the total number of vector elements that can be used -** as part of the range comparison. -** -** For example, if the query is: -** -** WHERE a = ? AND (b, c, d) > (?, ?, ?) -** -** and the index: -** -** CREATE INDEX ... ON (a, b, c, d, e) -** -** then this function would be invoked with nEq=1. The value returned in -** this case is 3. -*/ -static int whereRangeVectorLen( - Parse *pParse, /* Parsing context */ - int iCur, /* Cursor open on pIdx */ - Index *pIdx, /* The index to be used for a inequality constraint */ - int nEq, /* Number of prior equality constraints on same index */ - WhereTerm *pTerm /* The vector inequality constraint */ - ){ - int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft); - int i; - - nCmp = MIN(nCmp, (pIdx->nColumn - nEq)); - for (i=1; ipExpr->pLeft->x.pList->a[i].pExpr; - Expr *pRhs = pTerm->pExpr->pRight; - if (pRhs->flags & EP_xIsSelect) { - pRhs = pRhs->x.pSelect->pEList->a[i].pExpr; - } else { - pRhs = pRhs->x.pList->a[i].pExpr; - } - - /* Check that the LHS of the comparison is a column reference to - ** the right column of the right source table. And that the sort - ** order of the index column is the same as the sort order of the - ** leftmost index column. */ - if (pLhs->op!=TK_COLUMN - || pLhs->iTable!=iCur - || pLhs->iColumn!=pIdx->aiColumn[i+nEq] - || pIdx->aSortOrder[i+nEq]!=pIdx->aSortOrder[nEq] - ) { - break; - } - - testcase( pLhs->iColumn==XN_ROWID ); - aff = sqlite3CompareAffinity(pRhs, sqlite3ExprAffinity(pLhs)); - idxaff = sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn); - if (aff!=idxaff) break; - - pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); - if (pColl==0) break; - if (sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq])) break; - } - return i; -} - -/* -** Adjust the cost C by the costMult facter T. This only occurs if -** compiled with -DSQLITE_ENABLE_COSTMULT -*/ -#ifdef SQLITE_ENABLE_COSTMULT -# define ApplyCostMultiplier(C,T) C += T -#else -# define ApplyCostMultiplier(C,T) -#endif - -/* -** We have so far matched pBuilder->pNew->u.btree.nEq terms of the -** index pIndex. Try to match one more. -** -** When this function is called, pBuilder->pNew->nOut contains the -** number of rows expected to be visited by filtering using the nEq -** terms only. If it is modified, this value is restored before this -** function returns. -** -** If pProbe->idxType==SQLITE_IDXTYPE_IPK, that means pIndex is -** a fake index used for the INTEGER PRIMARY KEY. -*/ -static int whereLoopAddBtreeIndex( - WhereLoopBuilder *pBuilder, /* The WhereLoop factory */ - struct SrcList_item *pSrc, /* FROM clause term being analyzed */ - Index *pProbe, /* An index on pSrc */ - LogEst nInMul /* log(Number of iterations due to IN) */ - ){ - WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyse context */ - Parse *pParse = pWInfo->pParse; /* Parsing context */ - sqlite3 *db = pParse->db; /* Database connection malloc context */ - WhereLoop *pNew; /* Template WhereLoop under construction */ - WhereTerm *pTerm; /* A WhereTerm under consideration */ - int opMask; /* Valid operators for constraints */ - WhereScan scan; /* Iterator for WHERE terms */ - Bitmask saved_prereq; /* Original value of pNew->prereq */ - u16 saved_nLTerm; /* Original value of pNew->nLTerm */ - u16 saved_nEq; /* Original value of pNew->u.btree.nEq */ - u16 saved_nBtm; /* Original value of pNew->u.btree.nBtm */ - u16 saved_nTop; /* Original value of pNew->u.btree.nTop */ - u16 saved_nSkip; /* Original value of pNew->nSkip */ - u32 saved_wsFlags; /* Original value of pNew->wsFlags */ - LogEst saved_nOut; /* Original value of pNew->nOut */ - int rc = SQLITE_OK; /* Return code */ - LogEst rSize; /* Number of rows in the table */ - LogEst rLogSize; /* Logarithm of table size */ - WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ - - pNew = pBuilder->pNew; - if (db->mallocFailed) return SQLITE_NOMEM_BKPT; - WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d\n", - pProbe->pTable->zName,pProbe->zName, pNew->u.btree.nEq)); - - assert((pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); - assert((pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); - if (pNew->wsFlags & WHERE_BTM_LIMIT) { - opMask = WO_LT|WO_LE; - } else { - assert( pNew->u.btree.nBtm==0 ); - opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; - } - if (pProbe->bUnordered) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); - - assert( pNew->u.btree.nEqnColumn ); - - saved_nEq = pNew->u.btree.nEq; - saved_nBtm = pNew->u.btree.nBtm; - saved_nTop = pNew->u.btree.nTop; - saved_nSkip = pNew->nSkip; - saved_nLTerm = pNew->nLTerm; - saved_wsFlags = pNew->wsFlags; - saved_prereq = pNew->prereq; - saved_nOut = pNew->nOut; - pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, saved_nEq, - opMask, pProbe); - pNew->rSetup = 0; - rSize = pProbe->aiRowLogEst[0]; - rLogSize = estLog(rSize); - for (; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)) { - u16 eOp = pTerm->eOperator; /* Shorthand for pTerm->eOperator */ - LogEst rCostIdx; - LogEst nOutUnadjusted; /* nOut before IN() and WHERE adjustments */ - int nIn = 0; -#ifdef SQLITE_ENABLE_STAT4 - int nRecValid = pBuilder->nRecValid; -#endif - if ((eOp==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0) - && indexColumnNotNull(pProbe, saved_nEq) - ) { - continue; /* ignore IS [NOT] NULL constraints on NOT NULL columns */ - } - if (pTerm->prereqRight & pNew->maskSelf) continue; - - /* Do not allow the upper bound of a LIKE optimization range constraint - ** to mix with a lower range bound from some other source */ - if (pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT) continue; - - /* Do not allow constraints from the WHERE clause to be used by the - ** right table of a LEFT JOIN. Only constraints in the ON clause are - ** allowed */ - if ((pSrc->fg.jointype & JT_LEFT)!=0 - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - ) { - continue; - } - - if (IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1) { - pBuilder->bldFlags |= SQLITE_BLDF_UNIQUE; - } else { - pBuilder->bldFlags |= SQLITE_BLDF_INDEXED; - } - pNew->wsFlags = saved_wsFlags; - pNew->u.btree.nEq = saved_nEq; - pNew->u.btree.nBtm = saved_nBtm; - pNew->u.btree.nTop = saved_nTop; - pNew->nLTerm = saved_nLTerm; - if (whereLoopResize(db, pNew, pNew->nLTerm+1)) break; /* OOM */ - pNew->aLTerm[pNew->nLTerm++] = pTerm; - pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf; - - assert( nInMul==0 - || (pNew->wsFlags & WHERE_COLUMN_NULL)!=0 - || (pNew->wsFlags & WHERE_COLUMN_IN)!=0 - || (pNew->wsFlags & WHERE_SKIPSCAN)!=0 - ); - - if (eOp & WO_IN) { - Expr *pExpr = pTerm->pExpr; - if (ExprHasProperty(pExpr, EP_xIsSelect)) { - /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */ - int i; - nIn = 46; assert( 46==sqlite3LogEst(25)); - - /* The expression may actually be of the form (x, y) IN (SELECT...). - ** In this case there is a separate term for each of (x) and (y). - ** However, the nIn multiplier should only be applied once, not once - ** for each such term. The following loop checks that pTerm is the - ** first such term in use, and sets nIn back to 0 if it is not. */ - for (i=0; inLTerm-1; i++) { - if (pNew->aLTerm[i] && pNew->aLTerm[i]->pExpr==pExpr) nIn = 0; - } - } else if (ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr)) { - /* "x IN (value, value, ...)" */ - nIn = sqlite3LogEst(pExpr->x.pList->nExpr); - assert( nIn>0 ); /* RHS always has 2 or more terms... The parser - ** changes "x IN (?)" into "x=?". */ - } - if (pProbe->hasStat1) { - LogEst M, logK, safetyMargin; - /* Let: - ** N = the total number of rows in the table - ** K = the number of entries on the RHS of the IN operator - ** M = the number of rows in the table that match terms to the - ** to the left in the same index. If the IN operator is on - ** the left-most index column, M==N. - ** - ** Given the definitions above, it is better to omit the IN operator - ** from the index lookup and instead do a scan of the M elements, - ** testing each scanned row against the IN operator separately, if: - ** - ** M*log(K) < K*log(N) - ** - ** Our estimates for M, K, and N might be inaccurate, so we build in - ** a safety margin of 2 (LogEst: 10) that favors using the IN operator - ** with the index, as using an index has better worst-case behavior. - ** If we do not have real sqlite_stat1 data, always prefer to use - ** the index. - */ - M = pProbe->aiRowLogEst[saved_nEq]; - logK = estLog(nIn); - safetyMargin = 10; /* TUNING: extra weight for indexed IN */ - if (M + logK + safetyMargin < nIn + rLogSize) { - WHERETRACE(0x40, - ("Scan preferred over IN operator on column %d of \"%s\" (%d<%d)\n", - saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); - continue; - } else { - WHERETRACE(0x40, - ("IN operator preferred on column %d of \"%s\" (%d>=%d)\n", - saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); - } - } - pNew->wsFlags |= WHERE_COLUMN_IN; - } else if (eOp & (WO_EQ|WO_IS)) { - int iCol = pProbe->aiColumn[saved_nEq]; - pNew->wsFlags |= WHERE_COLUMN_EQ; - assert( saved_nEq==pNew->u.btree.nEq ); - if (iCol==XN_ROWID - || (iCol>=0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1) - ) { - if (iCol==XN_ROWID || pProbe->uniqNotNull - || (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ) - ) { - pNew->wsFlags |= WHERE_ONEROW; - } else { - pNew->wsFlags |= WHERE_UNQ_WANTED; - } - } - } else if (eOp & WO_ISNULL) { - pNew->wsFlags |= WHERE_COLUMN_NULL; - } else if (eOp & (WO_GT|WO_GE)) { - testcase( eOp & WO_GT ); - testcase( eOp & WO_GE ); - pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; - pNew->u.btree.nBtm = whereRangeVectorLen( - pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm - ); - pBtm = pTerm; - pTop = 0; - if (pTerm->wtFlags & TERM_LIKEOPT) { - /* Range contraints that come from the LIKE optimization are - ** always used in pairs. */ - pTop = &pTerm[1]; - assert((pTop-(pTerm->pWC->a))pWC->nTerm ); - assert( pTop->wtFlags & TERM_LIKEOPT ); - assert( pTop->eOperator==WO_LT ); - if (whereLoopResize(db, pNew, pNew->nLTerm+1)) break; /* OOM */ - pNew->aLTerm[pNew->nLTerm++] = pTop; - pNew->wsFlags |= WHERE_TOP_LIMIT; - pNew->u.btree.nTop = 1; - } - } else { - assert( eOp & (WO_LT|WO_LE)); - testcase( eOp & WO_LT ); - testcase( eOp & WO_LE ); - pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT; - pNew->u.btree.nTop = whereRangeVectorLen( - pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm - ); - pTop = pTerm; - pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ? - pNew->aLTerm[pNew->nLTerm-2] : 0; - } - - /* At this point pNew->nOut is set to the number of rows expected to - ** be visited by the index scan before considering term pTerm, or the - ** values of nIn and nInMul. In other words, assuming that all - ** "x IN(...)" terms are replaced with "x = ?". This block updates - ** the value of pNew->nOut to account for pTerm (but not nIn/nInMul). */ - assert( pNew->nOut==saved_nOut ); - if (pNew->wsFlags & WHERE_COLUMN_RANGE) { - /* Adjust nOut using stat4 data. Or, if there is no stat4 - ** data, using some other estimate. */ - whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew); - } else { - int nEq = ++pNew->u.btree.nEq; - assert( eOp & (WO_ISNULL|WO_EQ|WO_IN|WO_IS)); - - assert( pNew->nOut==saved_nOut ); - if (pTerm->truthProb<=0 && pProbe->aiColumn[saved_nEq]>=0) { - assert((eOp & WO_IN) || nIn==0 ); - testcase( eOp & WO_IN ); - pNew->nOut += pTerm->truthProb; - pNew->nOut -= nIn; - } else { -#ifdef SQLITE_ENABLE_STAT4 - tRowcnt nOut = 0; - if (nInMul==0 - && pProbe->nSample - && pNew->u.btree.nEq<=pProbe->nSampleCol - && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect)) - && OptimizationEnabled(db, SQLITE_Stat4) - ) { - Expr *pExpr = pTerm->pExpr; - if ((eOp & (WO_EQ|WO_ISNULL|WO_IS))!=0) { - testcase( eOp & WO_EQ ); - testcase( eOp & WO_IS ); - testcase( eOp & WO_ISNULL ); - rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut); - } else { - rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut); - } - if (rc==SQLITE_NOTFOUND) rc = SQLITE_OK; - if (rc!=SQLITE_OK) break; /* Jump out of the pTerm loop */ - if (nOut) { - pNew->nOut = sqlite3LogEst(nOut); - if (pNew->nOut>saved_nOut) pNew->nOut = saved_nOut; - pNew->nOut -= nIn; - } - } - if (nOut==0) -#endif - { - pNew->nOut += (pProbe->aiRowLogEst[nEq] - pProbe->aiRowLogEst[nEq-1]); - if (eOp & WO_ISNULL) { - /* TUNING: If there is no likelihood() value, assume that a - ** "col IS NULL" expression matches twice as many rows - ** as (col=?). */ - pNew->nOut += 10; - } - } - } - } - - /* Set rCostIdx to the cost of visiting selected rows in index. Add - ** it to pNew->rRun, which is currently set to the cost of the index - ** seek only. Then, if this is a non-covering index, add the cost of - ** visiting the rows in the main table. */ - rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow; - pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx); - if ((pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0) { - pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16); - } - ApplyCostMultiplier(pNew->rRun, pProbe->pTable->costMult); - - nOutUnadjusted = pNew->nOut; - pNew->rRun += nInMul + nIn; - pNew->nOut += nInMul + nIn; - whereLoopOutputAdjust(pBuilder->pWC, pNew, rSize); - rc = whereLoopInsert(pBuilder, pNew); - - if (pNew->wsFlags & WHERE_COLUMN_RANGE) { - pNew->nOut = saved_nOut; - } else { - pNew->nOut = nOutUnadjusted; - } - - if ((pNew->wsFlags & WHERE_TOP_LIMIT)==0 - && pNew->u.btree.nEqnColumn - ) { - whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); - } - pNew->nOut = saved_nOut; -#ifdef SQLITE_ENABLE_STAT4 - pBuilder->nRecValid = nRecValid; -#endif - } - pNew->prereq = saved_prereq; - pNew->u.btree.nEq = saved_nEq; - pNew->u.btree.nBtm = saved_nBtm; - pNew->u.btree.nTop = saved_nTop; - pNew->nSkip = saved_nSkip; - pNew->wsFlags = saved_wsFlags; - pNew->nOut = saved_nOut; - pNew->nLTerm = saved_nLTerm; - - /* Consider using a skip-scan if there are no WHERE clause constraints - ** available for the left-most terms of the index, and if the average - ** number of repeats in the left-most terms is at least 18. - ** - ** The magic number 18 is selected on the basis that scanning 17 rows - ** is almost always quicker than an index seek (even though if the index - ** contains fewer than 2^17 rows we assume otherwise in other parts of - ** the code). And, even if it is not, it should not be too much slower. - ** On the other hand, the extra seeks could end up being significantly - ** more expensive. */ - assert( 42==sqlite3LogEst(18)); - if (saved_nEq==saved_nSkip - && saved_nEq+1nKeyCol - && pProbe->noSkipScan==0 - && OptimizationEnabled(db, SQLITE_SkipScan) - && pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */ - && (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK - ) { - LogEst nIter; - pNew->u.btree.nEq++; - pNew->nSkip++; - pNew->aLTerm[pNew->nLTerm++] = 0; - pNew->wsFlags |= WHERE_SKIPSCAN; - nIter = pProbe->aiRowLogEst[saved_nEq] - pProbe->aiRowLogEst[saved_nEq+1]; - pNew->nOut -= nIter; - /* TUNING: Because uncertainties in the estimates for skip-scan queries, - ** add a 1.375 fudge factor to make skip-scan slightly less likely. */ - nIter += 5; - whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter + nInMul); - pNew->nOut = saved_nOut; - pNew->u.btree.nEq = saved_nEq; - pNew->nSkip = saved_nSkip; - pNew->wsFlags = saved_wsFlags; - } - - WHERETRACE(0x800, ("END %s.addBtreeIdx(%s), nEq=%d, rc=%d\n", - pProbe->pTable->zName, pProbe->zName, saved_nEq, rc)); - return rc; -} - -/* -** Return True if it is possible that pIndex might be useful in -** implementing the ORDER BY clause in pBuilder. -** -** Return False if pBuilder does not contain an ORDER BY clause or -** if there is no way for pIndex to be useful in implementing that -** ORDER BY clause. -*/ -static int indexMightHelpWithOrderBy( - WhereLoopBuilder *pBuilder, - Index *pIndex, - int iCursor - ){ - ExprList *pOB; - ExprList *aColExpr; - int ii, jj; - - if (pIndex->bUnordered) return 0; - if ((pOB = pBuilder->pWInfo->pOrderBy)==0) return 0; - for (ii=0; iinExpr; ii++) { - Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr); - if (pExpr->op==TK_COLUMN && pExpr->iTable==iCursor) { - if (pExpr->iColumn<0) return 1; - for (jj=0; jjnKeyCol; jj++) { - if (pExpr->iColumn==pIndex->aiColumn[jj]) return 1; - } - } else if ((aColExpr = pIndex->aColExpr)!=0) { - for (jj=0; jjnKeyCol; jj++) { - if (pIndex->aiColumn[jj]!=XN_EXPR) continue; - if (sqlite3ExprCompareSkip(pExpr,aColExpr->a[jj].pExpr,iCursor)==0) { - return 1; - } - } - } - } - return 0; -} - -/* Check to see if a partial index with pPartIndexWhere can be used -** in the current query. Return true if it can be and false if not. -*/ -static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){ - int i; - WhereTerm *pTerm; - Parse *pParse = pWC->pWInfo->pParse; - while (pWhere->op==TK_AND) { - if (!whereUsablePartialIndex(iTab,pWC,pWhere->pLeft)) return 0; - pWhere = pWhere->pRight; - } - if (pParse->db->flags & SQLITE_EnableQPSG) pParse = 0; - for (i=0, pTerm=pWC->a; inTerm; i++, pTerm++) { - Expr *pExpr = pTerm->pExpr; - if ((!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) - && sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab) - ) { - return 1; - } - } - return 0; -} - -/* -** Add all WhereLoop objects for a single table of the join where the table -** is identified by pBuilder->pNew->iTab. That table is guaranteed to be -** a b-tree table, not a virtual table. -** -** The costs (WhereLoop.rRun) of the b-tree loops added by this function -** are calculated as follows: -** -** For a full scan, assuming the table (or index) contains nRow rows: -** -** cost = nRow * 3.0 // full-table scan -** cost = nRow * K // scan of covering index -** cost = nRow * (K+3.0) // scan of non-covering index -** -** where K is a value between 1.1 and 3.0 set based on the relative -** estimated average size of the index and table records. -** -** For an index scan, where nVisit is the number of index rows visited -** by the scan, and nSeek is the number of seek operations required on -** the index b-tree: -** -** cost = nSeek * (log(nRow) + K * nVisit) // covering index -** cost = nSeek * (log(nRow) + (K+3.0) * nVisit) // non-covering index -** -** Normally, nSeek is 1. nSeek values greater than 1 come about if the -** WHERE clause includes "x IN (....)" terms used in place of "x=?". Or when -** implicit "x IN (SELECT x FROM tbl)" terms are added for skip-scans. -** -** The estimated values (nRow, nVisit, nSeek) often contain a large amount -** of uncertainty. For this reason, scoring is designed to pick plans that -** "do the least harm" if the estimates are inaccurate. For example, a -** log(nRow) factor is omitted from a non-covering index scan in order to -** bias the scoring in favor of using an index, since the worst-case -** performance of using an index is far better than the worst-case performance -** of a full table scan. -*/ -static int whereLoopAddBtree( - WhereLoopBuilder *pBuilder, /* WHERE clause information */ - Bitmask mPrereq /* Extra prerequesites for using this table */ - ){ - WhereInfo *pWInfo; /* WHERE analysis context */ - Index *pProbe; /* An index we are evaluating */ - Index sPk; /* A fake index object for the primary key */ - LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */ - i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */ - SrcList *pTabList; /* The FROM clause */ - struct SrcList_item *pSrc; /* The FROM clause btree term to add */ - WhereLoop *pNew; /* Template WhereLoop object */ - int rc = SQLITE_OK; /* Return code */ - int iSortIdx = 1; /* Index number */ - int b; /* A boolean value */ - LogEst rSize; /* number of rows in the table */ - LogEst rLogSize; /* Logarithm of the number of rows in the table */ - WhereClause *pWC; /* The parsed WHERE clause */ - Table *pTab; /* Table being queried */ - - pNew = pBuilder->pNew; - pWInfo = pBuilder->pWInfo; - pTabList = pWInfo->pTabList; - pSrc = pTabList->a + pNew->iTab; - pTab = pSrc->pTab; - pWC = pBuilder->pWC; - assert( !IsVirtual(pSrc->pTab)); - - if (pSrc->pIBIndex) { - /* An INDEXED BY clause specifies a particular index to use */ - pProbe = pSrc->pIBIndex; - } else if (!HasRowid(pTab)) { - pProbe = pTab->pIndex; - } else { - /* There is no INDEXED BY clause. Create a fake Index object in local - ** variable sPk to represent the rowid primary key index. Make this - ** fake index the first in a chain of Index objects with all of the real - ** indices to follow */ - Index *pFirst; /* First of real indices on the table */ - memset(&sPk, 0, sizeof(Index)); - sPk.nKeyCol = 1; - sPk.nColumn = 1; - sPk.aiColumn = &aiColumnPk; - sPk.aiRowLogEst = aiRowEstPk; - sPk.onError = OE_Replace; - sPk.pTable = pTab; - sPk.szIdxRow = pTab->szTabRow; - sPk.idxType = SQLITE_IDXTYPE_IPK; - aiRowEstPk[0] = pTab->nRowLogEst; - aiRowEstPk[1] = 0; - pFirst = pSrc->pTab->pIndex; - if (pSrc->fg.notIndexed==0) { - /* The real indices of the table are only considered if the - ** NOT INDEXED qualifier is omitted from the FROM clause */ - sPk.pNext = pFirst; - } - pProbe = &sPk; - } - rSize = pTab->nRowLogEst; - rLogSize = estLog(rSize); - -#ifndef SQLITE_OMIT_AUTOMATIC_INDEX - /* Automatic indexes */ - if (!pBuilder->pOrSet /* Not part of an OR optimization */ - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 - && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 - && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */ - && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ - && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */ - && !pSrc->fg.isCorrelated /* Not a correlated subquery */ - && !pSrc->fg.isRecursive /* Not a recursive common table expression. */ - ) { - /* Generate auto-index WhereLoops */ - WhereTerm *pTerm; - WhereTerm *pWCEnd = pWC->a + pWC->nTerm; - for (pTerm=pWC->a; rc==SQLITE_OK && pTermprereqRight & pNew->maskSelf) continue; - if (termCanDriveIndex(pTerm, pSrc, 0)) { - pNew->u.btree.nEq = 1; - pNew->nSkip = 0; - pNew->u.btree.pIndex = 0; - pNew->nLTerm = 1; - pNew->aLTerm[0] = pTerm; - /* TUNING: One-time cost for computing the automatic index is - ** estimated to be X*N*log2(N) where N is the number of rows in - ** the table being indexed and where X is 7 (LogEst=28) for normal - ** tables or 0.5 (LogEst=-10) for views and subqueries. The value - ** of X is smaller for views and subqueries so that the query planner - ** will be more aggressive about generating automatic indexes for - ** those objects, since there is no opportunity to add schema - ** indexes on subqueries and views. */ - pNew->rSetup = rLogSize + rSize; - if (pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0) { - pNew->rSetup += 28; - } else { - pNew->rSetup -= 10; - } - ApplyCostMultiplier(pNew->rSetup, pTab->costMult); - if (pNew->rSetup<0) pNew->rSetup = 0; - /* TUNING: Each index lookup yields 20 rows in the table. This - ** is more than the usual guess of 10 rows, since we have no way - ** of knowing how selective the index will ultimately be. It would - ** not be unreasonable to make this value much larger. */ - pNew->nOut = 43; assert( 43==sqlite3LogEst(20)); - pNew->rRun = sqlite3LogEstAdd(rLogSize,pNew->nOut); - pNew->wsFlags = WHERE_AUTO_INDEX; - pNew->prereq = mPrereq | pTerm->prereqRight; - rc = whereLoopInsert(pBuilder, pNew); - } - } - } -#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ - - /* Loop over all indices. If there was an INDEXED BY clause, then only - ** consider index pProbe. */ - for (; rc==SQLITE_OK && pProbe; - pProbe=(pSrc->pIBIndex ? 0 : pProbe->pNext), iSortIdx++ - ) { - if (pProbe->pPartIdxWhere!=0 - && !whereUsablePartialIndex(pSrc->iCursor, pWC, pProbe->pPartIdxWhere)) { - testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */ - continue; /* Partial index inappropriate for this query */ - } - if (pProbe->bNoQuery) continue; - rSize = pProbe->aiRowLogEst[0]; - pNew->u.btree.nEq = 0; - pNew->u.btree.nBtm = 0; - pNew->u.btree.nTop = 0; - pNew->nSkip = 0; - pNew->nLTerm = 0; - pNew->iSortIdx = 0; - pNew->rSetup = 0; - pNew->prereq = mPrereq; - pNew->nOut = rSize; - pNew->u.btree.pIndex = pProbe; - b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); - /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ - assert((pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 ); - if (pProbe->idxType==SQLITE_IDXTYPE_IPK) { - /* Integer primary key index */ - pNew->wsFlags = WHERE_IPK; - - /* Full table scan */ - pNew->iSortIdx = b ? iSortIdx : 0; - /* TUNING: Cost of full table scan is (N*3.0). */ - pNew->rRun = rSize + 16; - ApplyCostMultiplier(pNew->rRun, pTab->costMult); - whereLoopOutputAdjust(pWC, pNew, rSize); - rc = whereLoopInsert(pBuilder, pNew); - pNew->nOut = rSize; - if (rc) break; - } else { - Bitmask m; - if (pProbe->isCovering) { - pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; - m = 0; - } else { - m = pSrc->colUsed & pProbe->colNotIdxed; - pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED; - } - - /* Full scan via index */ - if (b - || !HasRowid(pTab) - || pProbe->pPartIdxWhere!=0 - || (m==0 - && pProbe->bUnordered==0 - && (pProbe->szIdxRowszTabRow) - && (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 - && sqlite3GlobalConfig.bUseCis - && OptimizationEnabled(pWInfo->pParse->db, SQLITE_CoverIdxScan) - ) - ) { - pNew->iSortIdx = b ? iSortIdx : 0; - - /* The cost of visiting the index rows is N*K, where K is - ** between 1.1 and 3.0, depending on the relative sizes of the - ** index and table rows. */ - pNew->rRun = rSize + 1 + (15*pProbe->szIdxRow)/pTab->szTabRow; - if (m!=0) { - /* If this is a non-covering index scan, add in the cost of - ** doing table lookups. The cost will be 3x the number of - ** lookups. Take into account WHERE clause terms that can be - ** satisfied using just the index, and that do not require a - ** table lookup. */ - LogEst nLookup = rSize + 16; /* Base cost: N*3 */ - int ii; - int iCur = pSrc->iCursor; - WhereClause *pWC2 = &pWInfo->sWC; - for (ii=0; iinTerm; ii++) { - WhereTerm *pTerm = &pWC2->a[ii]; - if (!sqlite3ExprCoveredByIndex(pTerm->pExpr, iCur, pProbe)) { - break; - } - /* pTerm can be evaluated using just the index. So reduce - ** the expected number of table lookups accordingly */ - if (pTerm->truthProb<=0) { - nLookup += pTerm->truthProb; - } else { - nLookup--; - if (pTerm->eOperator & (WO_EQ|WO_IS)) nLookup -= 19; - } - } - - pNew->rRun = sqlite3LogEstAdd(pNew->rRun, nLookup); - } - ApplyCostMultiplier(pNew->rRun, pTab->costMult); - whereLoopOutputAdjust(pWC, pNew, rSize); - rc = whereLoopInsert(pBuilder, pNew); - pNew->nOut = rSize; - if (rc) break; - } - } - - pBuilder->bldFlags = 0; - rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); - if (pBuilder->bldFlags==SQLITE_BLDF_INDEXED) { - /* If a non-unique index is used, or if a prefix of the key for - ** unique index is used (making the index functionally non-unique) - ** then the sqlite_stat1 data becomes important for scoring the - ** plan */ - pTab->tabFlags |= TF_StatsUsed; - } -#ifdef SQLITE_ENABLE_STAT4 - sqlite3Stat4ProbeFree(pBuilder->pRec); - pBuilder->nRecValid = 0; - pBuilder->pRec = 0; -#endif - } - return rc; -} - -#ifndef SQLITE_OMIT_VIRTUALTABLE - -/* -** Argument pIdxInfo is already populated with all constraints that may -** be used by the virtual table identified by pBuilder->pNew->iTab. This -** function marks a subset of those constraints usable, invokes the -** xBestIndex method and adds the returned plan to pBuilder. -** -** A constraint is marked usable if: -** -** * Argument mUsable indicates that its prerequisites are available, and -** -** * It is not one of the operators specified in the mExclude mask passed -** as the fourth argument (which in practice is either WO_IN or 0). -** -** Argument mPrereq is a mask of tables that must be scanned before the -** virtual table in question. These are added to the plans prerequisites -** before it is added to pBuilder. -** -** Output parameter *pbIn is set to true if the plan added to pBuilder -** uses one or more WO_IN terms, or false otherwise. -*/ -static int whereLoopAddVirtualOne( - WhereLoopBuilder *pBuilder, - Bitmask mPrereq, /* Mask of tables that must be used. */ - Bitmask mUsable, /* Mask of usable tables */ - u16 mExclude, /* Exclude terms using these operators */ - sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */ - u16 mNoOmit, /* Do not omit these constraints */ - int *pbIn /* OUT: True if plan uses an IN(...) op */ - ){ - WhereClause *pWC = pBuilder->pWC; - struct sqlite3_index_constraint *pIdxCons; - struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage; - int i; - int mxTerm; - int rc = SQLITE_OK; - WhereLoop *pNew = pBuilder->pNew; - Parse *pParse = pBuilder->pWInfo->pParse; - struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab]; - int nConstraint = pIdxInfo->nConstraint; - - assert((mUsable & mPrereq)==mPrereq ); - *pbIn = 0; - pNew->prereq = mPrereq; - - /* Set the usable flag on the subset of constraints identified by - ** arguments mUsable and mExclude. */ - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - for (i=0; ia[pIdxCons->iTermOffset]; - pIdxCons->usable = 0; - if ((pTerm->prereqRight & mUsable)==pTerm->prereqRight - && (pTerm->eOperator & mExclude)==0 - ) { - pIdxCons->usable = 1; - } - } - - /* Initialize the output fields of the sqlite3_index_info structure */ - memset(pUsage, 0, sizeof(pUsage[0])*nConstraint); - assert( pIdxInfo->needToFreeIdxStr==0 ); - pIdxInfo->idxStr = 0; - pIdxInfo->idxNum = 0; - pIdxInfo->orderByConsumed = 0; - pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; - pIdxInfo->estimatedRows = 25; - pIdxInfo->idxFlags = 0; - pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; - - /* Invoke the virtual table xBestIndex() method */ - rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); - if (rc) { - if (rc==SQLITE_CONSTRAINT) { - /* If the xBestIndex method returns SQLITE_CONSTRAINT, that means - ** that the particular combination of parameters provided is unusable. - ** Make no entries in the loop table. - */ - WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n")); - return SQLITE_OK; - } - return rc; - } - - mxTerm = -1; - assert( pNew->nLSlot>=nConstraint ); - for (i=0; iaLTerm[i] = 0; - pNew->u.vtab.omitMask = 0; - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - for (i=0; i=0) { - WhereTerm *pTerm; - int j = pIdxCons->iTermOffset; - if (iTerm>=nConstraint - || j<0 - || j>=pWC->nTerm - || pNew->aLTerm[iTerm]!=0 - || pIdxCons->usable==0 - ) { - sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); - testcase( pIdxInfo->needToFreeIdxStr ); - return SQLITE_ERROR; - } - testcase( iTerm==nConstraint-1 ); - testcase( j==0 ); - testcase( j==pWC->nTerm-1 ); - pTerm = &pWC->a[j]; - pNew->prereq |= pTerm->prereqRight; - assert( iTermnLSlot ); - pNew->aLTerm[iTerm] = pTerm; - if (iTerm>mxTerm) mxTerm = iTerm; - testcase( iTerm==15 ); - testcase( iTerm==16 ); - if (iTerm<16 && pUsage[i].omit) pNew->u.vtab.omitMask |= 1<eOperator & WO_IN)!=0) { - /* A virtual table that is constrained by an IN clause may not - ** consume the ORDER BY clause because (1) the order of IN terms - ** is not necessarily related to the order of output terms and - ** (2) Multiple outputs from a single IN value will not merge - ** together. */ - pIdxInfo->orderByConsumed = 0; - pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; - *pbIn = 1; assert((mExclude & WO_IN)==0 ); - } - } - } - pNew->u.vtab.omitMask &= ~mNoOmit; - - pNew->nLTerm = mxTerm+1; - for (i=0; i<=mxTerm; i++) { - if (pNew->aLTerm[i]==0) { - /* The non-zero argvIdx values must be contiguous. Raise an - ** error if they are not */ - sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); - testcase( pIdxInfo->needToFreeIdxStr ); - return SQLITE_ERROR; - } - } - assert( pNew->nLTerm<=pNew->nLSlot ); - pNew->u.vtab.idxNum = pIdxInfo->idxNum; - pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr; - pIdxInfo->needToFreeIdxStr = 0; - pNew->u.vtab.idxStr = pIdxInfo->idxStr; - pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ? - pIdxInfo->nOrderBy : 0); - pNew->rSetup = 0; - pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost); - pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows); - - /* Set the WHERE_ONEROW flag if the xBestIndex() method indicated - ** that the scan will visit at most one row. Clear it otherwise. */ - if (pIdxInfo->idxFlags & SQLITE_INDEX_SCAN_UNIQUE) { - pNew->wsFlags |= WHERE_ONEROW; - } else { - pNew->wsFlags &= ~WHERE_ONEROW; - } - rc = whereLoopInsert(pBuilder, pNew); - if (pNew->u.vtab.needFree) { - sqlite3_free(pNew->u.vtab.idxStr); - pNew->u.vtab.needFree = 0; - } - WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n", - *pbIn, (sqlite3_uint64)mPrereq, - (sqlite3_uint64)(pNew->prereq & ~mPrereq))); - - return rc; -} - -/* -** If this function is invoked from within an xBestIndex() callback, it -** returns a pointer to a buffer containing the name of the collation -** sequence associated with element iCons of the sqlite3_index_info.aConstraint -** array. Or, if iCons is out of range or there is no active xBestIndex -** call, return NULL. -*/ -const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){ - HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; - const char *zRet = 0; - if (iCons>=0 && iConsnConstraint) { - CollSeq *pC = 0; - int iTerm = pIdxInfo->aConstraint[iCons].iTermOffset; - Expr *pX = pHidden->pWC->a[iTerm].pExpr; - if (pX->pLeft) { - pC = sqlite3BinaryCompareCollSeq(pHidden->pParse, pX->pLeft, pX->pRight); - } - zRet = (pC ? pC->zName : sqlite3StrBINARY); - } - return zRet; -} - -/* -** Add all WhereLoop objects for a table of the join identified by -** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table. -** -** If there are no LEFT or CROSS JOIN joins in the query, both mPrereq and -** mUnusable are set to 0. Otherwise, mPrereq is a mask of all FROM clause -** entries that occur before the virtual table in the FROM clause and are -** separated from it by at least one LEFT or CROSS JOIN. Similarly, the -** mUnusable mask contains all FROM clause entries that occur after the -** virtual table and are separated from it by at least one LEFT or -** CROSS JOIN. -** -** For example, if the query were: -** -** ... FROM t1, t2 LEFT JOIN t3, t4, vt CROSS JOIN t5, t6; -** -** then mPrereq corresponds to (t1, t2) and mUnusable to (t5, t6). -** -** All the tables in mPrereq must be scanned before the current virtual -** table. So any terms for which all prerequisites are satisfied by -** mPrereq may be specified as "usable" in all calls to xBestIndex. -** Conversely, all tables in mUnusable must be scanned after the current -** virtual table, so any terms for which the prerequisites overlap with -** mUnusable should always be configured as "not-usable" for xBestIndex. -*/ -static int whereLoopAddVirtual( - WhereLoopBuilder *pBuilder, /* WHERE clause information */ - Bitmask mPrereq, /* Tables that must be scanned before this one */ - Bitmask mUnusable /* Tables that must be scanned after this one */ - ){ - int rc = SQLITE_OK; /* Return code */ - WhereInfo *pWInfo; /* WHERE analysis context */ - Parse *pParse; /* The parsing context */ - WhereClause *pWC; /* The WHERE clause */ - struct SrcList_item *pSrc; /* The FROM clause term to search */ - sqlite3_index_info *p; /* Object to pass to xBestIndex() */ - int nConstraint; /* Number of constraints in p */ - int bIn; /* True if plan uses IN(...) operator */ - WhereLoop *pNew; - Bitmask mBest; /* Tables used by best possible plan */ - u16 mNoOmit; - - assert((mPrereq & mUnusable)==0 ); - pWInfo = pBuilder->pWInfo; - pParse = pWInfo->pParse; - pWC = pBuilder->pWC; - pNew = pBuilder->pNew; - pSrc = &pWInfo->pTabList->a[pNew->iTab]; - assert( IsVirtual(pSrc->pTab)); - p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy, - &mNoOmit); - if (p==0) return SQLITE_NOMEM_BKPT; - pNew->rSetup = 0; - pNew->wsFlags = WHERE_VIRTUALTABLE; - pNew->nLTerm = 0; - pNew->u.vtab.needFree = 0; - nConstraint = p->nConstraint; - if (whereLoopResize(pParse->db, pNew, nConstraint)) { - sqlite3DbFree(pParse->db, p); - return SQLITE_NOMEM_BKPT; - } - - /* First call xBestIndex() with all constraints usable. */ - WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); - WHERETRACE(0x40, (" VirtualOne: all usable\n")); - rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); - - /* If the call to xBestIndex() with all terms enabled produced a plan - ** that does not require any source tables (IOW: a plan with mBest==0) - ** and does not use an IN(...) operator, then there is no point in making - ** any further calls to xBestIndex() since they will all return the same - ** result (if the xBestIndex() implementation is sane). */ - if (rc==SQLITE_OK && ((mBest = (pNew->prereq & ~mPrereq))!=0 || bIn)) { - int seenZero = 0; /* True if a plan with no prereqs seen */ - int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */ - Bitmask mPrev = 0; - Bitmask mBestNoIn = 0; - - /* If the plan produced by the earlier call uses an IN(...) term, call - ** xBestIndex again, this time with IN(...) terms disabled. */ - if (bIn) { - WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n")); - rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn); - assert( bIn==0 ); - mBestNoIn = pNew->prereq & ~mPrereq; - if (mBestNoIn==0) { - seenZero = 1; - seenZeroNoIN = 1; - } - } - - /* Call xBestIndex once for each distinct value of (prereqRight & ~mPrereq) - ** in the set of terms that apply to the current virtual table. */ - while (rc==SQLITE_OK) { - int i; - Bitmask mNext = ALLBITS; - assert( mNext>0 ); - for (i=0; ia[p->aConstraint[i].iTermOffset].prereqRight & ~mPrereq - ); - if (mThis>mPrev && mThisprereq==mPrereq) { - seenZero = 1; - if (bIn==0) seenZeroNoIN = 1; - } - } - - /* If the calls to xBestIndex() in the above loop did not find a plan - ** that requires no source tables at all (i.e. one guaranteed to be - ** usable), make a call here with all source tables disabled */ - if (rc==SQLITE_OK && seenZero==0) { - WHERETRACE(0x40, (" VirtualOne: all disabled\n")); - rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn); - if (bIn==0) seenZeroNoIN = 1; - } - - /* If the calls to xBestIndex() have so far failed to find a plan - ** that requires no source tables at all and does not use an IN(...) - ** operator, make a final call to obtain one here. */ - if (rc==SQLITE_OK && seenZeroNoIN==0) { - WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); - rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); - } - } - - if (p->needToFreeIdxStr) sqlite3_free(p->idxStr); - sqlite3DbFreeNN(pParse->db, p); - WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); - return rc; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -/* -** Add WhereLoop entries to handle OR terms. This works for either -** btrees or virtual tables. -*/ -static int whereLoopAddOr( - WhereLoopBuilder *pBuilder, - Bitmask mPrereq, - Bitmask mUnusable - ){ - WhereInfo *pWInfo = pBuilder->pWInfo; - WhereClause *pWC; - WhereLoop *pNew; - WhereTerm *pTerm, *pWCEnd; - int rc = SQLITE_OK; - int iCur; - WhereClause tempWC; - WhereLoopBuilder sSubBuild; - WhereOrSet sSum, sCur; - struct SrcList_item *pItem; - - pWC = pBuilder->pWC; - pWCEnd = pWC->a + pWC->nTerm; - pNew = pBuilder->pNew; - memset(&sSum, 0, sizeof(sSum)); - pItem = pWInfo->pTabList->a + pNew->iTab; - iCur = pItem->iCursor; - - for (pTerm=pWC->a; pTermeOperator & WO_OR)!=0 - && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0 - ) { - WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; - WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; - WhereTerm *pOrTerm; - int once = 1; - int i, j; - - sSubBuild = *pBuilder; - sSubBuild.pOrderBy = 0; - sSubBuild.pOrSet = &sCur; - - WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm)); - for (pOrTerm=pOrWC->a; pOrTermeOperator & WO_AND)!=0) { - sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc; - } else if (pOrTerm->leftCursor==iCur) { - tempWC.pWInfo = pWC->pWInfo; - tempWC.pOuter = pWC; - tempWC.op = TK_AND; - tempWC.nTerm = 1; - tempWC.a = pOrTerm; - sSubBuild.pWC = &tempWC; - } else { - continue; - } - sCur.n = 0; -#ifdef WHERETRACE_ENABLED - WHERETRACE(0x200, ("OR-term %d of %p has %d subterms:\n", - (int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm)); - if (sqlite3WhereTrace & 0x400) { - sqlite3WhereClausePrint(sSubBuild.pWC); - } -#endif -#ifndef SQLITE_OMIT_VIRTUALTABLE - if (IsVirtual(pItem->pTab)) { - rc = whereLoopAddVirtual(&sSubBuild, mPrereq, mUnusable); - } else -#endif - { - rc = whereLoopAddBtree(&sSubBuild, mPrereq); - } - if (rc==SQLITE_OK) { - rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable); - } - assert( rc==SQLITE_OK || sCur.n==0 ); - if (sCur.n==0) { - sSum.n = 0; - break; - } else if (once) { - whereOrMove(&sSum, &sCur); - once = 0; - } else { - WhereOrSet sPrev; - whereOrMove(&sPrev, &sSum); - sSum.n = 0; - for (i=0; inLTerm = 1; - pNew->aLTerm[0] = pTerm; - pNew->wsFlags = WHERE_MULTI_OR; - pNew->rSetup = 0; - pNew->iSortIdx = 0; - memset(&pNew->u, 0, sizeof(pNew->u)); - for (i=0; rc==SQLITE_OK && irRun = sSum.a[i].rRun + 1; - pNew->nOut = sSum.a[i].nOut; - pNew->prereq = sSum.a[i].prereq; - rc = whereLoopInsert(pBuilder, pNew); - } - WHERETRACE(0x200, ("End processing OR-clause %p\n", pTerm)); - } - } - return rc; -} - -/* -** Add all WhereLoop objects for all tables -*/ -static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ - WhereInfo *pWInfo = pBuilder->pWInfo; - Bitmask mPrereq = 0; - Bitmask mPrior = 0; - int iTab; - SrcList *pTabList = pWInfo->pTabList; - struct SrcList_item *pItem; - struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel]; - sqlite3 *db = pWInfo->pParse->db; - int rc = SQLITE_OK; - WhereLoop *pNew; - u8 priorJointype = 0; - - /* Loop over the tables in the join, from left to right */ - pNew = pBuilder->pNew; - whereLoopInit(pNew); - pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT; - for (iTab=0, pItem=pTabList->a; pItemiTab = iTab; - pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; - pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); - if (((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0) { - /* This condition is true when pItem is the FROM clause term on the - ** right-hand-side of a LEFT or CROSS JOIN. */ - mPrereq = mPrior; - } - priorJointype = pItem->fg.jointype; -#ifndef SQLITE_OMIT_VIRTUALTABLE - if (IsVirtual(pItem->pTab)) { - struct SrcList_item *p; - for (p=&pItem[1]; pfg.jointype & (JT_LEFT|JT_CROSS))) { - mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); - } - } - rc = whereLoopAddVirtual(pBuilder, mPrereq, mUnusable); - } else -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - { - rc = whereLoopAddBtree(pBuilder, mPrereq); - } - if (rc==SQLITE_OK && pBuilder->pWC->hasOr) { - rc = whereLoopAddOr(pBuilder, mPrereq, mUnusable); - } - mPrior |= pNew->maskSelf; - if (rc || db->mallocFailed) { - if (rc==SQLITE_DONE) { - /* We hit the query planner search limit set by iPlanLimit */ - sqlite3_log(SQLITE_WARNING, "abbreviated query algorithm search"); - rc = SQLITE_OK; - } else { - break; - } - } - } - - whereLoopClear(db, pNew); - return rc; -} - -/* -** Examine a WherePath (with the addition of the extra WhereLoop of the 6th -** parameters) to see if it outputs rows in the requested ORDER BY -** (or GROUP BY) without requiring a separate sort operation. Return N: -** -** N>0: N terms of the ORDER BY clause are satisfied -** N==0: No terms of the ORDER BY clause are satisfied -** N<0: Unknown yet how many terms of ORDER BY might be satisfied. -** -** Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as -** strict. With GROUP BY and DISTINCT the only requirement is that -** equivalent rows appear immediately adjacent to one another. GROUP BY -** and DISTINCT do not require rows to appear in any particular order as long -** as equivalent rows are grouped together. Thus for GROUP BY and DISTINCT -** the pOrderBy terms can be matched in any order. With ORDER BY, the -** pOrderBy terms must be matched in strict left-to-right order. -*/ -static i8 wherePathSatisfiesOrderBy( - WhereInfo *pWInfo, /* The WHERE clause */ - ExprList *pOrderBy, /* ORDER BY or GROUP BY or DISTINCT clause to check */ - WherePath *pPath, /* The WherePath to check */ - u16 wctrlFlags, /* WHERE_GROUPBY or _DISTINCTBY or _ORDERBY_LIMIT */ - u16 nLoop, /* Number of entries in pPath->aLoop[] */ - WhereLoop *pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */ - Bitmask *pRevMask /* OUT: Mask of WhereLoops to run in reverse order */ - ){ - u8 revSet; /* True if rev is known */ - u8 rev; /* Composite sort order */ - u8 revIdx; /* Index sort order */ - u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */ - u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */ - u8 isMatch; /* iColumn matches a term of the ORDER BY clause */ - u16 eqOpMask; /* Allowed equality operators */ - u16 nKeyCol; /* Number of key columns in pIndex */ - u16 nColumn; /* Total number of ordered columns in the index */ - u16 nOrderBy; /* Number terms in the ORDER BY clause */ - int iLoop; /* Index of WhereLoop in pPath being processed */ - int i, j; /* Loop counters */ - int iCur; /* Cursor number for current WhereLoop */ - int iColumn; /* A column number within table iCur */ - WhereLoop *pLoop = 0; /* Current WhereLoop being processed. */ - WhereTerm *pTerm; /* A single term of the WHERE clause */ - Expr *pOBExpr; /* An expression from the ORDER BY clause */ - CollSeq *pColl; /* COLLATE function from an ORDER BY clause term */ - Index *pIndex; /* The index associated with pLoop */ - sqlite3 *db = pWInfo->pParse->db; /* Database connection */ - Bitmask obSat = 0; /* Mask of ORDER BY terms satisfied so far */ - Bitmask obDone; /* Mask of all ORDER BY terms */ - Bitmask orderDistinctMask; /* Mask of all well-ordered loops */ - Bitmask ready; /* Mask of inner loops */ - - /* - ** We say the WhereLoop is "one-row" if it generates no more than one - ** row of output. A WhereLoop is one-row if all of the following are true: - ** (a) All index columns match with WHERE_COLUMN_EQ. - ** (b) The index is unique - ** Any WhereLoop with an WHERE_COLUMN_EQ constraint on the rowid is one-row. - ** Every one-row WhereLoop will have the WHERE_ONEROW bit set in wsFlags. - ** - ** We say the WhereLoop is "order-distinct" if the set of columns from - ** that WhereLoop that are in the ORDER BY clause are different for every - ** row of the WhereLoop. Every one-row WhereLoop is automatically - ** order-distinct. A WhereLoop that has no columns in the ORDER BY clause - ** is not order-distinct. To be order-distinct is not quite the same as being - ** UNIQUE since a UNIQUE column or index can have multiple rows that - ** are NULL and NULL values are equivalent for the purpose of order-distinct. - ** To be order-distinct, the columns must be UNIQUE and NOT NULL. - ** - ** The rowid for a table is always UNIQUE and NOT NULL so whenever the - ** rowid appears in the ORDER BY clause, the corresponding WhereLoop is - ** automatically order-distinct. - */ - - assert( pOrderBy!=0 ); - if (nLoop && OptimizationDisabled(db, SQLITE_OrderByIdxJoin)) return 0; - - nOrderBy = pOrderBy->nExpr; - testcase( nOrderBy==BMS-1 ); - if (nOrderBy>BMS-1) return 0; /* Cannot optimize overly large ORDER BYs */ - isOrderDistinct = 1; - obDone = MASKBIT(nOrderBy)-1; - orderDistinctMask = 0; - ready = 0; - eqOpMask = WO_EQ | WO_IS | WO_ISNULL; - if (wctrlFlags & WHERE_ORDERBY_LIMIT) eqOpMask |= WO_IN; - for (iLoop=0; isOrderDistinct && obSat0) ready |= pLoop->maskSelf; - if (iLoopaLoop[iLoop]; - if (wctrlFlags & WHERE_ORDERBY_LIMIT) continue; - } else { - pLoop = pLast; - } - if (pLoop->wsFlags & WHERE_VIRTUALTABLE) { - if (pLoop->u.vtab.isOrdered) obSat = obDone; - break; - } else if (wctrlFlags & WHERE_DISTINCTBY) { - pLoop->u.btree.nDistinctCol = 0; - } - iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; - - /* Mark off any ORDER BY term X that is a column in the table of - ** the current loop for which there is term in the WHERE - ** clause of the form X IS NULL or X=? that reference only outer - ** loops. - */ - for (i=0; ia[i].pExpr); - if (pOBExpr->op!=TK_COLUMN) continue; - if (pOBExpr->iTable!=iCur) continue; - pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, - ~ready, eqOpMask, 0); - if (pTerm==0) continue; - if (pTerm->eOperator==WO_IN) { - /* IN terms are only valid for sorting in the ORDER BY LIMIT - ** optimization, and then only if they are actually used - ** by the query plan */ - assert( wctrlFlags & WHERE_ORDERBY_LIMIT ); - for (j=0; jnLTerm && pTerm!=pLoop->aLTerm[j]; j++) {} - if (j>=pLoop->nLTerm) continue; - } - if ((pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0) { - if (sqlite3ExprCollSeqMatch(pWInfo->pParse, - pOrderBy->a[i].pExpr, pTerm->pExpr)==0) { - continue; - } - testcase( pTerm->pExpr->op==TK_IS ); - } - obSat |= MASKBIT(i); - } - - if ((pLoop->wsFlags & WHERE_ONEROW)==0) { - if (pLoop->wsFlags & WHERE_IPK) { - pIndex = 0; - nKeyCol = 0; - nColumn = 1; - } else if ((pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered) { - return 0; - } else { - nKeyCol = pIndex->nKeyCol; - nColumn = pIndex->nColumn; - assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable)); - assert( pIndex->aiColumn[nColumn-1]==XN_ROWID - || !HasRowid(pIndex->pTable)); - isOrderDistinct = IsUniqueIndex(pIndex) - && (pLoop->wsFlags & WHERE_SKIPSCAN)==0; - } - - /* Loop through all columns of the index and deal with the ones - ** that are not constrained by == or IN. - */ - rev = revSet = 0; - distinctColumns = 0; - for (j=0; j=pLoop->u.btree.nEq - || (pLoop->aLTerm[j]==0)==(jnSkip) - ); - if (ju.btree.nEq && j>=pLoop->nSkip) { - u16 eOp = pLoop->aLTerm[j]->eOperator; - - /* Skip over == and IS and ISNULL terms. (Also skip IN terms when - ** doing WHERE_ORDERBY_LIMIT processing). - ** - ** If the current term is a column of an ((?,?) IN (SELECT...)) - ** expression for which the SELECT returns more than one column, - ** check that it is the only column used by this loop. Otherwise, - ** if it is one of two or more, none of the columns can be - ** considered to match an ORDER BY term. */ - if ((eOp & eqOpMask)!=0) { - if (eOp & WO_ISNULL) { - testcase( isOrderDistinct ); - isOrderDistinct = 0; - } - continue; - } else if (ALWAYS(eOp & WO_IN)) { - /* ALWAYS() justification: eOp is an equality operator due to the - ** ju.btree.nEq constraint above. Any equality other - ** than WO_IN is captured by the previous "if". So this one - ** always has to be WO_IN. */ - Expr *pX = pLoop->aLTerm[j]->pExpr; - for (i=j+1; iu.btree.nEq; i++) { - if (pLoop->aLTerm[i]->pExpr==pX) { - assert((pLoop->aLTerm[i]->eOperator & WO_IN)); - bOnce = 0; - break; - } - } - } - } - - /* Get the column number in the table (iColumn) and sort order - ** (revIdx) for the j-th column of the index. - */ - if (pIndex) { - iColumn = pIndex->aiColumn[j]; - revIdx = pIndex->aSortOrder[j]; - if (iColumn==pIndex->pTable->iPKey) iColumn = XN_ROWID; - } else { - iColumn = XN_ROWID; - revIdx = 0; - } - - /* An unconstrained column that might be NULL means that this - ** WhereLoop is not well-ordered - */ - if (isOrderDistinct - && iColumn>=0 - && j>=pLoop->u.btree.nEq - && pIndex->pTable->aCol[iColumn].notNull==0 - ) { - isOrderDistinct = 0; - } - - /* Find the ORDER BY term that corresponds to the j-th column - ** of the index and mark that ORDER BY term off - */ - isMatch = 0; - for (i=0; bOnce && ia[i].pExpr); - testcase( wctrlFlags & WHERE_GROUPBY ); - testcase( wctrlFlags & WHERE_DISTINCTBY ); - if ((wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0) bOnce = 0; - if (iColumn>=XN_ROWID) { - if (pOBExpr->op!=TK_COLUMN) continue; - if (pOBExpr->iTable!=iCur) continue; - if (pOBExpr->iColumn!=iColumn) continue; - } else { - Expr *pIdxExpr = pIndex->aColExpr->a[j].pExpr; - if (sqlite3ExprCompareSkip(pOBExpr, pIdxExpr, iCur)) { - continue; - } - } - if (iColumn!=XN_ROWID) { - pColl = sqlite3ExprNNCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr); - if (sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0) continue; - } - if (wctrlFlags & WHERE_DISTINCTBY) { - pLoop->u.btree.nDistinctCol = j+1; - } - isMatch = 1; - break; - } - if (isMatch && (wctrlFlags & WHERE_GROUPBY)==0) { - /* Make sure the sort order is compatible in an ORDER BY clause. - ** Sort order is irrelevant for a GROUP BY clause. */ - if (revSet) { - if ((rev ^ revIdx)!=pOrderBy->a[i].sortOrder) isMatch = 0; - } else { - rev = revIdx ^ pOrderBy->a[i].sortOrder; - if (rev) *pRevMask |= MASKBIT(iLoop); - revSet = 1; - } - } - if (isMatch) { - if (iColumn==XN_ROWID) { - testcase( distinctColumns==0 ); - distinctColumns = 1; - } - obSat |= MASKBIT(i); - if ((wctrlFlags & WHERE_ORDERBY_MIN) && j==pLoop->u.btree.nEq) { - pLoop->wsFlags |= WHERE_MIN_ORDERED; - } - } else { - /* No match found */ - if (j==0 || jmaskSelf; - for (i=0; ia[i].pExpr; - mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p); - if (mTerm==0 && !sqlite3ExprIsConstant(p)) continue; - if ((mTerm&~orderDistinctMask)==0) { - obSat |= MASKBIT(i); - } - } - } - } /* End the loop over all WhereLoops from outer-most down to inner-most */ - if (obSat==obDone) return (i8)nOrderBy; - if (!isOrderDistinct) { - for (i=nOrderBy-1; i>0; i--) { - Bitmask m = MASKBIT(i) - 1; - if ((obSat&m)==m) return i; - } - return 0; - } - return -1; -} - - -/* -** If the WHERE_GROUPBY flag is set in the mask passed to sqlite3WhereBegin(), -** the planner assumes that the specified pOrderBy list is actually a GROUP -** BY clause - and so any order that groups rows as required satisfies the -** request. -** -** Normally, in this case it is not possible for the caller to determine -** whether or not the rows are really being delivered in sorted order, or -** just in some other order that provides the required grouping. However, -** if the WHERE_SORTBYGROUP flag is also passed to sqlite3WhereBegin(), then -** this function may be called on the returned WhereInfo object. It returns -** true if the rows really will be sorted in the specified order, or false -** otherwise. -** -** For example, assuming: -** -** CREATE INDEX i1 ON t1(x, Y); -** -** then -** -** SELECT * FROM t1 GROUP BY x,y ORDER BY x,y; -- IsSorted()==1 -** SELECT * FROM t1 GROUP BY y,x ORDER BY y,x; -- IsSorted()==0 -*/ -int sqlite3WhereIsSorted(WhereInfo *pWInfo){ - assert( pWInfo->wctrlFlags & WHERE_GROUPBY ); - assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP ); - return pWInfo->sorted; -} - -#ifdef WHERETRACE_ENABLED -/* For debugging use only: */ -static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){ - static char zName[65]; - int i; - for (i=0; iaLoop[i]->cId; } - if (pLast) zName[i++] = pLast->cId; - zName[i] = 0; - return zName; -} -#endif - -/* -** Return the cost of sorting nRow rows, assuming that the keys have -** nOrderby columns and that the first nSorted columns are already in -** order. -*/ -static LogEst whereSortingCost( - WhereInfo *pWInfo, - LogEst nRow, - int nOrderBy, - int nSorted - ){ - /* TUNING: Estimated cost of a full external sort, where N is - ** the number of rows to sort is: - ** - ** cost = (3.0 * N * log(N)). - ** - ** Or, if the order-by clause has X terms but only the last Y - ** terms are out of order, then block-sorting will reduce the - ** sorting cost to: - ** - ** cost = (3.0 * N * log(N)) * (Y/X) - ** - ** The (Y/X) term is implemented using stack variable rScale - ** below. */ - LogEst rScale, rSortCost; - assert( nOrderBy>0 && 66==sqlite3LogEst(100)); - rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; - rSortCost = nRow + rScale + 16; - - /* Multiple by log(M) where M is the number of output rows. - ** Use the LIMIT for M if it is smaller */ - if ((pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimitiLimit; - } - rSortCost += estLog(nRow); - return rSortCost; -} - -/* -** Given the list of WhereLoop objects at pWInfo->pLoops, this routine -** attempts to find the lowest cost path that visits each WhereLoop -** once. This path is then loaded into the pWInfo->a[].pWLoop fields. -** -** Assume that the total number of output rows that will need to be sorted -** will be nRowEst (in the 10*log2 representation). Or, ignore sorting -** costs if nRowEst==0. -** -** Return SQLITE_OK on success or SQLITE_NOMEM of a memory allocation -** error occurs. -*/ -static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - int mxChoice; /* Maximum number of simultaneous paths tracked */ - int nLoop; /* Number of terms in the join */ - Parse *pParse; /* Parsing context */ - sqlite3 *db; /* The database connection */ - int iLoop; /* Loop counter over the terms of the join */ - int ii, jj; /* Loop counters */ - int mxI = 0; /* Index of next entry to replace */ - int nOrderBy; /* Number of ORDER BY clause terms */ - LogEst mxCost = 0; /* Maximum cost of a set of paths */ - LogEst mxUnsorted = 0; /* Maximum unsorted cost of a set of path */ - int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */ - WherePath *aFrom; /* All nFrom paths at the previous level */ - WherePath *aTo; /* The nTo best paths at the current level */ - WherePath *pFrom; /* An element of aFrom[] that we are working on */ - WherePath *pTo; /* An element of aTo[] that we are working on */ - WhereLoop *pWLoop; /* One of the WhereLoop objects */ - WhereLoop **pX; /* Used to divy up the pSpace memory */ - LogEst *aSortCost = 0; /* Sorting and partial sorting costs */ - char *pSpace; /* Temporary memory used by this routine */ - int nSpace; /* Bytes of space allocated at pSpace */ - - pParse = pWInfo->pParse; - db = pParse->db; - nLoop = pWInfo->nLevel; - /* TUNING: For simple queries, only the best path is tracked. - ** For 2-way joins, the 5 best paths are followed. - ** For joins of 3 or more tables, track the 10 best paths */ - mxChoice = (nLoop<=1) ? 1 : (nLoop==2 ? 5 : 10); - assert( nLoop<=pWInfo->pTabList->nSrc ); - WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d)\n", nRowEst)); - - /* If nRowEst is zero and there is an ORDER BY clause, ignore it. In this - ** case the purpose of this call is to estimate the number of rows returned - ** by the overall query. Once this estimate has been obtained, the caller - ** will invoke this function a second time, passing the estimate as the - ** nRowEst parameter. */ - if (pWInfo->pOrderBy==0 || nRowEst==0) { - nOrderBy = 0; - } else { - nOrderBy = pWInfo->pOrderBy->nExpr; - } - - /* Allocate and initialize space for aTo, aFrom and aSortCost[] */ - nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2; - nSpace += sizeof(LogEst) * nOrderBy; - pSpace = sqlite3DbMallocRawNN(db, nSpace); - if (pSpace==0) return SQLITE_NOMEM_BKPT; - aTo = (WherePath*)pSpace; - aFrom = aTo+mxChoice; - memset(aFrom, 0, sizeof(aFrom[0])); - pX = (WhereLoop**)(aFrom+mxChoice); - for (ii=mxChoice*2, pFrom=aTo; ii>0; ii--, pFrom++, pX += nLoop) { - pFrom->aLoop = pX; - } - if (nOrderBy) { - /* If there is an ORDER BY clause and it is not being ignored, set up - ** space for the aSortCost[] array. Each element of the aSortCost array - ** is either zero - meaning it has not yet been initialized - or the - ** cost of sorting nRowEst rows of data where the first X terms of - ** the ORDER BY clause are already in order, where X is the array - ** index. */ - aSortCost = (LogEst*)pX; - memset(aSortCost, 0, sizeof(LogEst) * nOrderBy); - } - assert( aSortCost==0 || &pSpace[nSpace]==(char*)&aSortCost[nOrderBy] ); - assert( aSortCost!=0 || &pSpace[nSpace]==(char*)pX ); - - /* Seed the search with a single WherePath containing zero WhereLoops. - ** - ** TUNING: Do not let the number of iterations go above 28. If the cost - ** of computing an automatic index is not paid back within the first 28 - ** rows, then do not use the automatic index. */ - aFrom[0].nRow = MIN(pParse->nQueryLoop, 48); assert( 48==sqlite3LogEst(28)); - nFrom = 1; - assert( aFrom[0].isOrdered==0 ); - if (nOrderBy) { - /* If nLoop is zero, then there are no FROM terms in the query. Since - ** in this case the query may return a maximum of one row, the results - ** are already in the requested order. Set isOrdered to nOrderBy to - ** indicate this. Or, if nLoop is greater than zero, set isOrdered to - ** -1, indicating that the result set may or may not be ordered, - ** depending on the loops added to the current plan. */ - aFrom[0].isOrdered = nLoop>0 ? -1 : nOrderBy; - } - - /* Compute successively longer WherePaths using the previous generation - ** of WherePaths as the basis for the next. Keep track of the mxChoice - ** best paths at each generation */ - for (iLoop=0; iLooppLoops; pWLoop; pWLoop=pWLoop->pNextLoop) { - LogEst nOut; /* Rows visited by (pFrom+pWLoop) */ - LogEst rCost; /* Cost of path (pFrom+pWLoop) */ - LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */ - i8 isOrdered = pFrom->isOrdered; /* isOrdered for (pFrom+pWLoop) */ - Bitmask maskNew; /* Mask of src visited by (..) */ - Bitmask revMask = 0; /* Mask of rev-order loops for (..) */ - - if ((pWLoop->prereq & ~pFrom->maskLoop)!=0) continue; - if ((pWLoop->maskSelf & pFrom->maskLoop)!=0) continue; - if ((pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<3) { - /* Do not use an automatic index if the this loop is expected - ** to run less than 1.25 times. It is tempting to also exclude - ** automatic index usage on an outer loop, but sometimes an automatic - ** index is useful in the outer loop of a correlated subquery. */ - assert( 10==sqlite3LogEst(2)); - continue; - } - - /* At this point, pWLoop is a candidate to be the next loop. - ** Compute its cost */ - rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow); - rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted); - nOut = pFrom->nRow + pWLoop->nOut; - maskNew = pFrom->maskLoop | pWLoop->maskSelf; - if (isOrdered<0) { - isOrdered = wherePathSatisfiesOrderBy(pWInfo, - pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags, - iLoop, pWLoop, &revMask); - } else { - revMask = pFrom->revLoop; - } - if (isOrdered>=0 && isOrderedisOrdered^isOrdered)&0x80)==0" is equivalent - ** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range - ** of legal values for isOrdered, -1..64. - */ - for (jj=0, pTo=aTo; jjmaskLoop==maskNew - && ((pTo->isOrdered^isOrdered)&0x80)==0 - ) { - testcase( jj==nTo-1 ); - break; - } - } - if (jj>=nTo) { - /* None of the existing best-so-far paths match the candidate. */ - if (nTo>=mxChoice - && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted)) - ) { - /* The current candidate is no better than any of the mxChoice - ** paths currently in the best-so-far buffer. So discard - ** this candidate as not viable. */ -#ifdef WHERETRACE_ENABLED /* 0x4 */ - if (sqlite3WhereTrace&0x4) { - sqlite3DebugPrintf("Skip %s cost=%-3d,%3d,%3d order=%c\n", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, - isOrdered>=0 ? isOrdered+'0' : '?'); - } -#endif - continue; - } - /* If we reach this points it means that the new candidate path - ** needs to be added to the set of best-so-far paths. */ - if (nTo=0 ? isOrdered+'0' : '?'); - } -#endif - } else { - /* Control reaches here if best-so-far path pTo=aTo[jj] covers the - ** same set of loops and has the same isOrdered setting as the - ** candidate path. Check to see if the candidate should replace - ** pTo or if the candidate should be skipped. - ** - ** The conditional is an expanded vector comparison equivalent to: - ** (pTo->rCost,pTo->nRow,pTo->rUnsorted) <= (rCost,nOut,rUnsorted) - */ - if (pTo->rCostrCost==rCost - && (pTo->nRownRow==nOut && pTo->rUnsorted<=rUnsorted) - ) - ) - ) { -#ifdef WHERETRACE_ENABLED /* 0x4 */ - if (sqlite3WhereTrace&0x4) { - sqlite3DebugPrintf( - "Skip %s cost=%-3d,%3d,%3d order=%c", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, - isOrdered>=0 ? isOrdered+'0' : '?'); - sqlite3DebugPrintf(" vs %s cost=%-3d,%3d,%3d order=%c\n", - wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, - pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); - } -#endif - /* Discard the candidate path from further consideration */ - testcase( pTo->rCost==rCost ); - continue; - } - testcase( pTo->rCost==rCost+1 ); - /* Control reaches here if the candidate path is better than the - ** pTo path. Replace pTo with the candidate. */ -#ifdef WHERETRACE_ENABLED /* 0x4 */ - if (sqlite3WhereTrace&0x4) { - sqlite3DebugPrintf( - "Update %s cost=%-3d,%3d,%3d order=%c", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, - isOrdered>=0 ? isOrdered+'0' : '?'); - sqlite3DebugPrintf(" was %s cost=%-3d,%3d,%3d order=%c\n", - wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, - pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); - } -#endif - } - /* pWLoop is a winner. Add it to the set of best so far */ - pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf; - pTo->revLoop = revMask; - pTo->nRow = nOut; - pTo->rCost = rCost; - pTo->rUnsorted = rUnsorted; - pTo->isOrdered = isOrdered; - memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop); - pTo->aLoop[iLoop] = pWLoop; - if (nTo>=mxChoice) { - mxI = 0; - mxCost = aTo[0].rCost; - mxUnsorted = aTo[0].nRow; - for (jj=1, pTo=&aTo[1]; jjrCost>mxCost - || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted) - ) { - mxCost = pTo->rCost; - mxUnsorted = pTo->rUnsorted; - mxI = jj; - } - } - } - } - } - -#ifdef WHERETRACE_ENABLED /* >=2 */ - if (sqlite3WhereTrace & 0x02) { - sqlite3DebugPrintf("---- after round %d ----\n", iLoop); - for (ii=0, pTo=aTo; iirCost, pTo->nRow, - pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?'); - if (pTo->isOrdered>0) { - sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop); - } else { - sqlite3DebugPrintf("\n"); - } - } - } -#endif - - /* Swap the roles of aFrom and aTo for the next generation */ - pFrom = aTo; - aTo = aFrom; - aFrom = pFrom; - nFrom = nTo; - } - - if (nFrom==0) { - sqlite3ErrorMsg(pParse, "no query solution"); - sqlite3DbFreeNN(db, pSpace); - return SQLITE_ERROR; - } - - /* Find the lowest cost path. pFrom will be left pointing to that path */ - pFrom = aFrom; - for (ii=1; iirCost>aFrom[ii].rCost) pFrom = &aFrom[ii]; - } - assert( pWInfo->nLevel==nLoop ); - /* Load the lowest cost path into pWInfo */ - for (iLoop=0; iLoopa + iLoop; - pLevel->pWLoop = pWLoop = pFrom->aLoop[iLoop]; - pLevel->iFrom = pWLoop->iTab; - pLevel->iTabCur = pWInfo->pTabList->a[pLevel->iFrom].iCursor; - } - if ((pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 - && (pWInfo->wctrlFlags & WHERE_DISTINCTBY)==0 - && pWInfo->eDistinct==WHERE_DISTINCT_NOOP - && nRowEst - ) { - Bitmask notUsed; - int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pResultSet, pFrom, - WHERE_DISTINCTBY, nLoop-1, pFrom->aLoop[nLoop-1], ¬Used); - if (rc==pWInfo->pResultSet->nExpr) { - pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; - } - } - pWInfo->bOrderedInnerLoop = 0; - if (pWInfo->pOrderBy) { - if (pWInfo->wctrlFlags & WHERE_DISTINCTBY) { - if (pFrom->isOrdered==pWInfo->pOrderBy->nExpr) { - pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; - } - } else { - pWInfo->nOBSat = pFrom->isOrdered; - pWInfo->revMask = pFrom->revLoop; - if (pWInfo->nOBSat<=0) { - pWInfo->nOBSat = 0; - if (nLoop>0) { - u32 wsFlags = pFrom->aLoop[nLoop-1]->wsFlags; - if ((wsFlags & WHERE_ONEROW)==0 - && (wsFlags&(WHERE_IPK|WHERE_COLUMN_IN))!=(WHERE_IPK|WHERE_COLUMN_IN) - ) { - Bitmask m = 0; - int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom, - WHERE_ORDERBY_LIMIT, nLoop-1, pFrom->aLoop[nLoop-1], &m); - testcase( wsFlags & WHERE_IPK ); - testcase( wsFlags & WHERE_COLUMN_IN ); - if (rc==pWInfo->pOrderBy->nExpr) { - pWInfo->bOrderedInnerLoop = 1; - pWInfo->revMask = m; - } - } - } - } - } - if ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP) - && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr && nLoop>0 - ) { - Bitmask revMask = 0; - int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, - pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], &revMask - ); - assert( pWInfo->sorted==0 ); - if (nOrder==pWInfo->pOrderBy->nExpr) { - pWInfo->sorted = 1; - pWInfo->revMask = revMask; - } - } - } - - - pWInfo->nRowOut = pFrom->nRow; - - /* Free temporary memory and return success */ - sqlite3DbFreeNN(db, pSpace); - return SQLITE_OK; -} - -/* -** Most queries use only a single table (they are not joins) and have -** simple == constraints against indexed fields. This routine attempts -** to plan those simple cases using much less ceremony than the -** general-purpose query planner, and thereby yield faster sqlite3_prepare() -** times for the common case. -** -** Return non-zero on success, if this query can be handled by this -** no-frills query planner. Return zero if this query needs the -** general-purpose query planner. -*/ -static int whereShortCut(WhereLoopBuilder *pBuilder){ - WhereInfo *pWInfo; - struct SrcList_item *pItem; - WhereClause *pWC; - WhereTerm *pTerm; - WhereLoop *pLoop; - int iCur; - int j; - Table *pTab; - Index *pIdx; - - pWInfo = pBuilder->pWInfo; - if (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) return 0; - assert( pWInfo->pTabList->nSrc>=1 ); - pItem = pWInfo->pTabList->a; - pTab = pItem->pTab; - if (IsVirtual(pTab)) return 0; - if (pItem->fg.isIndexedBy) return 0; - iCur = pItem->iCursor; - pWC = &pWInfo->sWC; - pLoop = pBuilder->pNew; - pLoop->wsFlags = 0; - pLoop->nSkip = 0; - pTerm = sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0); - if (pTerm) { - testcase( pTerm->eOperator & WO_IS ); - pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW; - pLoop->aLTerm[0] = pTerm; - pLoop->nLTerm = 1; - pLoop->u.btree.nEq = 1; - /* TUNING: Cost of a rowid lookup is 10 */ - pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */ - } else { - for (pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext) { - int opMask; - assert( pLoop->aLTermSpace==pLoop->aLTerm ); - if (!IsUniqueIndex(pIdx) - || pIdx->pPartIdxWhere!=0 - || pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace) - ) continue; - opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ; - for (j=0; jnKeyCol; j++) { - pTerm = sqlite3WhereFindTerm(pWC, iCur, j, 0, opMask, pIdx); - if (pTerm==0) break; - testcase( pTerm->eOperator & WO_IS ); - pLoop->aLTerm[j] = pTerm; - } - if (j!=pIdx->nKeyCol) continue; - pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED; - if (pIdx->isCovering || (pItem->colUsed & pIdx->colNotIdxed)==0) { - pLoop->wsFlags |= WHERE_IDX_ONLY; - } - pLoop->nLTerm = j; - pLoop->u.btree.nEq = j; - pLoop->u.btree.pIndex = pIdx; - /* TUNING: Cost of a unique index lookup is 15 */ - pLoop->rRun = 39; /* 39==sqlite3LogEst(15) */ - break; - } - } - if (pLoop->wsFlags) { - pLoop->nOut = (LogEst)1; - pWInfo->a[0].pWLoop = pLoop; - assert( pWInfo->sMaskSet.n==1 && iCur==pWInfo->sMaskSet.ix[0] ); - pLoop->maskSelf = 1; /* sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); */ - pWInfo->a[0].iTabCur = iCur; - pWInfo->nRowOut = 1; - if (pWInfo->pOrderBy) pWInfo->nOBSat = pWInfo->pOrderBy->nExpr; - if (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) { - pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; - } -#ifdef SQLITE_DEBUG - pLoop->cId = '0'; -#endif - return 1; - } - return 0; -} - -/* -** Helper function for exprIsDeterministic(). -*/ -static int exprNodeIsDeterministic(Walker *pWalker, Expr *pExpr){ - if (pExpr->op==TK_FUNCTION && ExprHasProperty(pExpr, EP_ConstFunc)==0) { - pWalker->eCode = 0; - return WRC_Abort; - } - return WRC_Continue; -} - -/* -** Return true if the expression contains no non-deterministic SQL -** functions. Do not consider non-deterministic SQL functions that are -** part of sub-select statements. -*/ -static int exprIsDeterministic(Expr *p){ - Walker w; - memset(&w, 0, sizeof(w)); - w.eCode = 1; - w.xExprCallback = exprNodeIsDeterministic; - w.xSelectCallback = sqlite3SelectWalkFail; - sqlite3WalkExpr(&w, p); - return w.eCode; -} - -/* -** Generate the beginning of the loop used for WHERE clause processing. -** The return value is a pointer to an opaque structure that contains -** information needed to terminate the loop. Later, the calling routine -** should invoke sqlite3WhereEnd() with the return value of this function -** in order to complete the WHERE clause processing. -** -** If an error occurs, this routine returns NULL. -** -** The basic idea is to do a nested loop, one loop for each table in -** the FROM clause of a select. (INSERT and UPDATE statements are the -** same as a SELECT with only a single table in the FROM clause.) For -** example, if the SQL is this: -** -** SELECT * FROM t1, t2, t3 WHERE ...; -** -** Then the code generated is conceptually like the following: -** -** foreach row1 in t1 do \ Code generated -** foreach row2 in t2 do |-- by sqlite3WhereBegin() -** foreach row3 in t3 do / -** ... -** end \ Code generated -** end |-- by sqlite3WhereEnd() -** end / -** -** Note that the loops might not be nested in the order in which they -** appear in the FROM clause if a different order is better able to make -** use of indices. Note also that when the IN operator appears in -** the WHERE clause, it might result in additional nested loops for -** scanning through all values on the right-hand side of the IN. -** -** There are Btree cursors associated with each table. t1 uses cursor -** number pTabList->a[0].iCursor. t2 uses the cursor pTabList->a[1].iCursor. -** And so forth. This routine generates code to open those VDBE cursors -** and sqlite3WhereEnd() generates the code to close them. -** -** The code that sqlite3WhereBegin() generates leaves the cursors named -** in pTabList pointing at their appropriate entries. The [...] code -** can use OP_Column and OP_Rowid opcodes on these cursors to extract -** data from the various tables of the loop. -** -** If the WHERE clause is empty, the foreach loops must each scan their -** entire tables. Thus a three-way join is an O(N^3) operation. But if -** the tables have indices and there are terms in the WHERE clause that -** refer to those indices, a complete table scan can be avoided and the -** code will run much faster. Most of the work of this routine is checking -** to see if there are indices that can be used to speed up the loop. -** -** Terms of the WHERE clause are also used to limit which rows actually -** make it to the "..." in the middle of the loop. After each "foreach", -** terms of the WHERE clause that use only terms in that loop and outer -** loops are evaluated and if false a jump is made around all subsequent -** inner loops (or around the "..." if the test occurs within the inner- -** most loop) -** -** OUTER JOINS -** -** An outer join of tables t1 and t2 is conceptally coded as follows: -** -** foreach row1 in t1 do -** flag = 0 -** foreach row2 in t2 do -** start: -** ... -** flag = 1 -** end -** if flag==0 then -** move the row2 cursor to a null row -** goto start -** fi -** end -** -** ORDER BY CLAUSE PROCESSING -** -** pOrderBy is a pointer to the ORDER BY clause (or the GROUP BY clause -** if the WHERE_GROUPBY flag is set in wctrlFlags) of a SELECT statement -** if there is one. If there is no ORDER BY clause or if this routine -** is called from an UPDATE or DELETE statement, then pOrderBy is NULL. -** -** The iIdxCur parameter is the cursor number of an index. If -** WHERE_OR_SUBCLAUSE is set, iIdxCur is the cursor number of an index -** to use for OR clause processing. The WHERE clause should use this -** specific cursor. If WHERE_ONEPASS_DESIRED is set, then iIdxCur is -** the first cursor in an array of cursors for all indices. iIdxCur should -** be used to compute the appropriate cursor depending on which index is -** used. -*/ -WhereInfo *sqlite3WhereBegin( - Parse *pParse, /* The parser context */ - SrcList *pTabList, /* FROM clause: A list of all tables to be scanned */ - Expr *pWhere, /* The WHERE clause */ - ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ - ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */ - u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */ - int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number - ** If WHERE_USE_LIMIT, then the limit amount */ - ){ - int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */ - int nTabList; /* Number of elements in pTabList */ - WhereInfo *pWInfo; /* Will become the return value of this function */ - Vdbe *v = pParse->pVdbe; /* The virtual database engine */ - Bitmask notReady; /* Cursors that are not yet positioned */ - WhereLoopBuilder sWLB; /* The WhereLoop builder */ - WhereMaskSet *pMaskSet; /* The expression mask set */ - WhereLevel *pLevel; /* A single level in pWInfo->a[] */ - WhereLoop *pLoop; /* Pointer to a single WhereLoop object */ - int ii; /* Loop counter */ - sqlite3 *db; /* Database connection */ - int rc; /* Return code */ - u8 bFordelete = 0; /* OPFLAG_FORDELETE or zero, as appropriate */ - - assert((wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || ( - (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 - && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 - )); - - /* Only one of WHERE_OR_SUBCLAUSE or WHERE_USE_LIMIT */ - assert((wctrlFlags & WHERE_OR_SUBCLAUSE)==0 - || (wctrlFlags & WHERE_USE_LIMIT)==0 ); - - /* Variable initialization */ - db = pParse->db; - memset(&sWLB, 0, sizeof(sWLB)); - - /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ - testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); - if (pOrderBy && pOrderBy->nExpr>=BMS) pOrderBy = 0; - sWLB.pOrderBy = pOrderBy; - - /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via - ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ - if (OptimizationDisabled(db, SQLITE_DistinctOpt)) { - wctrlFlags &= ~WHERE_WANT_DISTINCT; - } - - /* The number of tables in the FROM clause is limited by the number of - ** bits in a Bitmask - */ - testcase( pTabList->nSrc==BMS ); - if (pTabList->nSrc>BMS) { - sqlite3ErrorMsg(pParse, "at most %d tables in a join", BMS); - return 0; - } - - /* This function normally generates a nested loop for all tables in - ** pTabList. But if the WHERE_OR_SUBCLAUSE flag is set, then we should - ** only generate code for the first table in pTabList and assume that - ** any cursors associated with subsequent tables are uninitialized. - */ - nTabList = (wctrlFlags & WHERE_OR_SUBCLAUSE) ? 1 : pTabList->nSrc; - - /* Allocate and initialize the WhereInfo structure that will become the - ** return value. A single allocation is used to store the WhereInfo - ** struct, the contents of WhereInfo.a[], the WhereClause structure - ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte - ** field (type Bitmask) it must be aligned on an 8-byte boundary on - ** some architectures. Hence the ROUND8() below. - */ - nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); - pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); - if (db->mallocFailed) { - sqlite3DbFree(db, pWInfo); - pWInfo = 0; - goto whereBeginError; - } - pWInfo->pParse = pParse; - pWInfo->pTabList = pTabList; - pWInfo->pOrderBy = pOrderBy; - pWInfo->pWhere = pWhere; - pWInfo->pResultSet = pResultSet; - pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1; - pWInfo->nLevel = nTabList; - pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(pParse); - pWInfo->wctrlFlags = wctrlFlags; - pWInfo->iLimit = iAuxArg; - pWInfo->savedNQueryLoop = pParse->nQueryLoop; - memset(&pWInfo->nOBSat, 0, - offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat)); - memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel)); - assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */ - pMaskSet = &pWInfo->sMaskSet; - sWLB.pWInfo = pWInfo; - sWLB.pWC = &pWInfo->sWC; - sWLB.pNew = (WhereLoop*)(((char*)pWInfo)+nByteWInfo); - assert( EIGHT_BYTE_ALIGNMENT(sWLB.pNew)); - whereLoopInit(sWLB.pNew); -#ifdef SQLITE_DEBUG - sWLB.pNew->cId = '*'; -#endif - - /* Split the WHERE clause into separate subexpressions where each - ** subexpression is separated by an AND operator. - */ - initMaskSet(pMaskSet); - sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo); - sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND); - - /* Special case: No FROM clause - */ - if (nTabList==0) { - if (pOrderBy) pWInfo->nOBSat = pOrderBy->nExpr; - if (wctrlFlags & WHERE_WANT_DISTINCT) { - pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; - } - ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); - } else { - /* Assign a bit from the bitmask to every term in the FROM clause. - ** - ** The N-th term of the FROM clause is assigned a bitmask of 1<nSrc tables in - ** pTabList, not just the first nTabList tables. nTabList is normally - ** equal to pTabList->nSrc but might be shortened to 1 if the - ** WHERE_OR_SUBCLAUSE flag is set. - */ - ii = 0; - do{ - createMask(pMaskSet, pTabList->a[ii].iCursor); - sqlite3WhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC); - }while ((++ii)nSrc); - #ifdef SQLITE_DEBUG - { - Bitmask mx = 0; - for (ii=0; iinSrc; ii++) { - Bitmask m = sqlite3WhereGetMask(pMaskSet, pTabList->a[ii].iCursor); - assert( m>=mx ); - mx = m; - } - } - #endif - } - - /* Analyze all of the subexpressions. */ - sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC); - if (db->mallocFailed) goto whereBeginError; - - /* Special case: WHERE terms that do not refer to any tables in the join - ** (constant expressions). Evaluate each such term, and jump over all the - ** generated code if the result is not true. - ** - ** Do not do this if the expression contains non-deterministic functions - ** that are not within a sub-select. This is not strictly required, but - ** preserves SQLite's legacy behaviour in the following two cases: - ** - ** FROM ... WHERE random()>0; -- eval random() once per row - ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall - */ - for (ii=0; iinTerm; ii++) { - WhereTerm *pT = &sWLB.pWC->a[ii]; - if (pT->wtFlags & TERM_VIRTUAL) continue; - if (pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr))) { - sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL); - pT->wtFlags |= TERM_CODED; - } - } - - if (wctrlFlags & WHERE_WANT_DISTINCT) { - if (isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet)) { - /* The DISTINCT marking is pointless. Ignore it. */ - pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; - } else if (pOrderBy==0) { - /* Try to ORDER BY the result set to make distinct processing easier */ - pWInfo->wctrlFlags |= WHERE_DISTINCTBY; - pWInfo->pOrderBy = pResultSet; - } - } - - /* Construct the WhereLoop objects */ -#if defined(WHERETRACE_ENABLED) - if (sqlite3WhereTrace & 0xffff) { - sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags); - if (wctrlFlags & WHERE_USE_LIMIT) { - sqlite3DebugPrintf(", limit: %d", iAuxArg); - } - sqlite3DebugPrintf(")\n"); - if (sqlite3WhereTrace & 0x100) { - Select sSelect; - memset(&sSelect, 0, sizeof(sSelect)); - sSelect.selFlags = SF_WhereBegin; - sSelect.pSrc = pTabList; - sSelect.pWhere = pWhere; - sSelect.pOrderBy = pOrderBy; - sSelect.pEList = pResultSet; - sqlite3TreeViewSelect(0, &sSelect, 0); - } - } - if (sqlite3WhereTrace & 0x100) { /* Display all terms of the WHERE clause */ - sqlite3WhereClausePrint(sWLB.pWC); - } -#endif - - if (nTabList!=1 || whereShortCut(&sWLB)==0) { - rc = whereLoopAddAll(&sWLB); - if (rc) goto whereBeginError; - -#ifdef WHERETRACE_ENABLED - if (sqlite3WhereTrace) { /* Display all of the WhereLoop objects */ - WhereLoop *p; - int i; - static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz" - "ABCDEFGHIJKLMNOPQRSTUVWYXZ"; - for (p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++) { - p->cId = zLabel[i%(sizeof(zLabel)-1)]; - whereLoopPrint(p, sWLB.pWC); - } - } -#endif - - wherePathSolver(pWInfo, 0); - if (db->mallocFailed) goto whereBeginError; - if (pWInfo->pOrderBy) { - wherePathSolver(pWInfo, pWInfo->nRowOut+1); - if (db->mallocFailed) goto whereBeginError; - } - } - if (pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0) { - pWInfo->revMask = ALLBITS; - } - if (pParse->nErr || NEVER(db->mallocFailed)) { - goto whereBeginError; - } -#ifdef WHERETRACE_ENABLED - if (sqlite3WhereTrace) { - sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); - if (pWInfo->nOBSat>0) { - sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask); - } - switch (pWInfo->eDistinct) { - case WHERE_DISTINCT_UNIQUE: { - sqlite3DebugPrintf(" DISTINCT=unique"); - break; - } - case WHERE_DISTINCT_ORDERED: { - sqlite3DebugPrintf(" DISTINCT=ordered"); - break; - } - case WHERE_DISTINCT_UNORDERED: { - sqlite3DebugPrintf(" DISTINCT=unordered"); - break; - } - } - sqlite3DebugPrintf("\n"); - for (ii=0; iinLevel; ii++) { - whereLoopPrint(pWInfo->a[ii].pWLoop, sWLB.pWC); - } - } -#endif - - /* Attempt to omit tables from the join that do not affect the result. - ** For a table to not affect the result, the following must be true: - ** - ** 1) The query must not be an aggregate. - ** 2) The table must be the RHS of a LEFT JOIN. - ** 3) Either the query must be DISTINCT, or else the ON or USING clause - ** must contain a constraint that limits the scan of the table to - ** at most a single row. - ** 4) The table must not be referenced by any part of the query apart - ** from its own USING or ON clause. - ** - ** For example, given: - ** - ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); - ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); - ** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); - ** - ** then table t2 can be omitted from the following: - ** - ** SELECT v1, v3 FROM t1 - ** LEFT JOIN t2 USING (t1.ipk=t2.ipk) - ** LEFT JOIN t3 USING (t1.ipk=t3.ipk) - ** - ** or from: - ** - ** SELECT DISTINCT v1, v3 FROM t1 - ** LEFT JOIN t2 - ** LEFT JOIN t3 USING (t1.ipk=t3.ipk) - */ - notReady = ~(Bitmask)0; - if (pWInfo->nLevel>=2 - && pResultSet!=0 /* guarantees condition (1) above */ - && OptimizationEnabled(db, SQLITE_OmitNoopJoin) - ) { - int i; - Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pResultSet); - if (sWLB.pOrderBy) { - tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy); - } - for (i=pWInfo->nLevel-1; i>=1; i--) { - WhereTerm *pTerm, *pEnd; - struct SrcList_item *pItem; - pLoop = pWInfo->a[i].pWLoop; - pItem = &pWInfo->pTabList->a[pLoop->iTab]; - if ((pItem->fg.jointype & JT_LEFT)==0) continue; - if ((wctrlFlags & WHERE_WANT_DISTINCT)==0 - && (pLoop->wsFlags & WHERE_ONEROW)==0 - ) { - continue; - } - if ((tabUsed & pLoop->maskSelf)!=0) continue; - pEnd = sWLB.pWC->a + sWLB.pWC->nTerm; - for (pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0) { - if (!ExprHasProperty(pTerm->pExpr, EP_FromJoin) - || pTerm->pExpr->iRightJoinTable!=pItem->iCursor - ) { - break; - } - } - } - if (pTerm drop loop %c not used\n", pLoop->cId)); - notReady &= ~pLoop->maskSelf; - for (pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0) { - pTerm->wtFlags |= TERM_CODED; - } - } - if (i!=pWInfo->nLevel-1) { - int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); - memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); - } - pWInfo->nLevel--; - nTabList--; - } - } - WHERETRACE(0xffff,("*** Optimizer Finished ***\n")); - pWInfo->pParse->nQueryLoop += pWInfo->nRowOut; - - /* If the caller is an UPDATE or DELETE statement that is requesting - ** to use a one-pass algorithm, determine if this is appropriate. - ** - ** A one-pass approach can be used if the caller has requested one - ** and either (a) the scan visits at most one row or (b) each - ** of the following are true: - ** - ** * the caller has indicated that a one-pass approach can be used - ** with multiple rows (by setting WHERE_ONEPASS_MULTIROW), and - ** * the table is not a virtual table, and - ** * either the scan does not use the OR optimization or the caller - ** is a DELETE operation (WHERE_DUPLICATES_OK is only specified - ** for DELETE). - ** - ** The last qualification is because an UPDATE statement uses - ** WhereInfo.aiCurOnePass[1] to determine whether or not it really can - ** use a one-pass approach, and this is not set accurately for scans - ** that use the OR optimization. - */ - assert((wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 ); - if ((wctrlFlags & WHERE_ONEPASS_DESIRED)!=0) { - int wsFlags = pWInfo->a[0].pWLoop->wsFlags; - int bOnerow = (wsFlags & WHERE_ONEROW)!=0; - assert( !(wsFlags & WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pTab)); - if (bOnerow || ( - 0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW) - && !IsVirtual(pTabList->a[0].pTab) - && (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK)) - )) { - pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; - if (HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY)) { - if (wctrlFlags & WHERE_ONEPASS_MULTIROW) { - bFordelete = OPFLAG_FORDELETE; - } - pWInfo->a[0].pWLoop->wsFlags = (wsFlags & ~WHERE_IDX_ONLY); - } - } - } - - /* Open all tables in the pTabList and any indices selected for - ** searching those tables. - */ - for (ii=0, pLevel=pWInfo->a; iia[pLevel->iFrom]; - pTab = pTabItem->pTab; - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - pLoop = pLevel->pWLoop; - if ((pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect) { - /* Do nothing */ - } else -#ifndef SQLITE_OMIT_VIRTUALTABLE - if ((pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0) { - const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); - int iCur = pTabItem->iCursor; - sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB); - } else if (IsVirtual(pTab)) { - /* noop */ - } else -#endif - if ((pLoop->wsFlags & WHERE_IDX_ONLY)==0 - && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0) { - int op = OP_OpenRead; - if (pWInfo->eOnePass!=ONEPASS_OFF) { - op = OP_OpenWrite; - pWInfo->aiCurOnePass[0] = pTabItem->iCursor; - }; - sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op); - assert( pTabItem->iCursor==pLevel->iTabCur ); - testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS-1 ); - testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS ); - if (pWInfo->eOnePass==ONEPASS_OFF && pTab->nColcolUsed; - int n = 0; - for (; b; b=b>>1, n++) {} - sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(n), P4_INT32); - assert( n<=pTab->nCol ); - } -#ifdef SQLITE_ENABLE_CURSOR_HINTS - if (pLoop->u.btree.pIndex!=0) { - sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete); - } else -#endif - { - sqlite3VdbeChangeP5(v, bFordelete); - } -#ifdef SQLITE_ENABLE_COLUMN_USED_MASK - sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, pTabItem->iCursor, 0, 0, - (const u8*)&pTabItem->colUsed, P4_INT64); -#endif - } else { - sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); - } - if (pLoop->wsFlags & WHERE_INDEXED) { - Index *pIx = pLoop->u.btree.pIndex; - int iIndexCur; - int op = OP_OpenRead; - /* iAuxArg is always set to a positive value if ONEPASS is possible */ - assert( iAuxArg!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 ); - if (!HasRowid(pTab) && IsPrimaryKeyIndex(pIx) - && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 - ) { - /* This is one term of an OR-optimization using the PRIMARY KEY of a - ** WITHOUT ROWID table. No need for a separate index */ - iIndexCur = pLevel->iTabCur; - op = 0; - } else if (pWInfo->eOnePass!=ONEPASS_OFF) { - Index *pJ = pTabItem->pTab->pIndex; - iIndexCur = iAuxArg; - assert( wctrlFlags & WHERE_ONEPASS_DESIRED ); - while (ALWAYS(pJ) && pJ!=pIx) { - iIndexCur++; - pJ = pJ->pNext; - } - op = OP_OpenWrite; - pWInfo->aiCurOnePass[1] = iIndexCur; - } else if (iAuxArg && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0) { - iIndexCur = iAuxArg; - op = OP_ReopenIdx; - } else { - iIndexCur = pParse->nTab++; - } - pLevel->iIdxCur = iIndexCur; - assert( pIx->pSchema==pTab->pSchema ); - assert( iIndexCur>=0 ); - if (op) { - sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb); - sqlite3VdbeSetP4KeyInfo(pParse, pIx); - if ((pLoop->wsFlags & WHERE_CONSTRAINT)!=0 - && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0 - && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 - && pWInfo->eDistinct!=WHERE_DISTINCT_ORDERED - ) { - sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */ - } - VdbeComment((v, "%s", pIx->zName)); -#ifdef SQLITE_ENABLE_COLUMN_USED_MASK - { - u64 colUsed = 0; - int ii, jj; - for (ii=0; iinColumn; ii++) { - jj = pIx->aiColumn[ii]; - if (jj<0) continue; - if (jj>63) jj = 63; - if ((pTabItem->colUsed & MASKBIT(jj))==0) continue; - colUsed |= ((u64)1)<<(ii<63 ? ii : 63); - } - sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, iIndexCur, 0, 0, - (u8*)&colUsed, P4_INT64); - } -#endif /* SQLITE_ENABLE_COLUMN_USED_MASK */ - } - } - if (iDb>=0) sqlite3CodeVerifySchema(pParse, iDb); - } - pWInfo->iTop = sqlite3VdbeCurrentAddr(v); - if (db->mallocFailed) goto whereBeginError; - - /* Generate the code to do the search. Each iteration of the for - ** loop below generates code for a single nested loop of the VM - ** program. - */ - for (ii=0; iia[ii]; - wsFlags = pLevel->pWLoop->wsFlags; -#ifndef SQLITE_OMIT_AUTOMATIC_INDEX - if ((pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0) { - constructAutomaticIndex(pParse, &pWInfo->sWC, - &pTabList->a[pLevel->iFrom], notReady, pLevel); - if (db->mallocFailed) goto whereBeginError; - } -#endif - addrExplain = sqlite3WhereExplainOneScan( - pParse, pTabList, pLevel, wctrlFlags - ); - pLevel->addrBody = sqlite3VdbeCurrentAddr(v); - notReady = sqlite3WhereCodeOneLoopStart(pParse,v,pWInfo,ii,pLevel,notReady); - pWInfo->iContinue = pLevel->addrCont; - if ((wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_OR_SUBCLAUSE)==0) { - sqlite3WhereAddScanStatus(v, pTabList, pLevel, addrExplain); - } - } - - /* Done. */ - VdbeModuleComment((v, "Begin WHERE-core")); - return pWInfo; - - /* Jump here if malloc fails */ -whereBeginError: - if (pWInfo) { - pParse->nQueryLoop = pWInfo->savedNQueryLoop; - whereInfoFree(db, pWInfo); - } - return 0; -} - -/* -** Part of sqlite3WhereEnd() will rewrite opcodes to reference the -** index rather than the main table. In SQLITE_DEBUG mode, we want -** to trace those changes if PRAGMA vdbe_addoptrace=on. This routine -** does that. -*/ -#ifndef SQLITE_DEBUG -# define OpcodeRewriteTrace(D,K,P) /* no-op */ -#else -# define OpcodeRewriteTrace(D,K,P) sqlite3WhereOpcodeRewriteTrace(D,K,P) -static void sqlite3WhereOpcodeRewriteTrace( - sqlite3 *db, - int pc, - VdbeOp *pOp - ){ - if ((db->flags & SQLITE_VdbeAddopTrace)==0) return; - sqlite3VdbePrintOp(0, pc, pOp); -} -#endif - -/* -** Generate the end of the WHERE loop. See comments on -** sqlite3WhereBegin() for additional information. -*/ -void sqlite3WhereEnd(WhereInfo *pWInfo){ - Parse *pParse = pWInfo->pParse; - Vdbe *v = pParse->pVdbe; - int i; - WhereLevel *pLevel; - WhereLoop *pLoop; - SrcList *pTabList = pWInfo->pTabList; - sqlite3 *db = pParse->db; - - /* Generate loop termination code. - */ - VdbeModuleComment((v, "End WHERE-core")); - for (i=pWInfo->nLevel-1; i>=0; i--) { - int addr; - pLevel = &pWInfo->a[i]; - pLoop = pLevel->pWLoop; - if (pLevel->op!=OP_Noop) { -#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT - int addrSeek = 0; - Index *pIdx; - int n; - if (pWInfo->eDistinct==WHERE_DISTINCT_ORDERED - && i==pWInfo->nLevel-1 /* Ticket [ef9318757b152e3] 2017-10-21 */ - && (pLoop->wsFlags & WHERE_INDEXED)!=0 - && (pIdx = pLoop->u.btree.pIndex)->hasStat1 - && (n = pLoop->u.btree.nDistinctCol)>0 - && pIdx->aiRowLogEst[n]>=36 - ) { - int r1 = pParse->nMem+1; - int j, op; - for (j=0; jiIdxCur, j, r1+j); - } - pParse->nMem += n+1; - op = pLevel->op==OP_Prev ? OP_SeekLT : OP_SeekGT; - addrSeek = sqlite3VdbeAddOp4Int(v, op, pLevel->iIdxCur, 0, r1, n); - VdbeCoverageIf(v, op==OP_SeekLT); - VdbeCoverageIf(v, op==OP_SeekGT); - sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2); - } -#endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ - /* The common case: Advance to the next row */ - sqlite3VdbeResolveLabel(v, pLevel->addrCont); - sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); - sqlite3VdbeChangeP5(v, pLevel->p5); - VdbeCoverage(v); - VdbeCoverageIf(v, pLevel->op==OP_Next); - VdbeCoverageIf(v, pLevel->op==OP_Prev); - VdbeCoverageIf(v, pLevel->op==OP_VNext); -#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT - if (addrSeek) sqlite3VdbeJumpHere(v, addrSeek); -#endif - } else { - sqlite3VdbeResolveLabel(v, pLevel->addrCont); - } - if (pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0) { - struct InLoop *pIn; - int j; - sqlite3VdbeResolveLabel(v, pLevel->addrNxt); - for (j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--) { - sqlite3VdbeJumpHere(v, pIn->addrInTop+1); - if (pIn->eEndLoopOp!=OP_Noop) { - if (pIn->nPrefix) { - assert( pLoop->wsFlags & WHERE_IN_EARLYOUT ); - sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur, - sqlite3VdbeCurrentAddr(v)+2, - pIn->iBase, pIn->nPrefix); - VdbeCoverage(v); - } - sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop); - VdbeCoverage(v); - VdbeCoverageIf(v, pIn->eEndLoopOp==OP_Prev); - VdbeCoverageIf(v, pIn->eEndLoopOp==OP_Next); - } - sqlite3VdbeJumpHere(v, pIn->addrInTop-1); - } - } - sqlite3VdbeResolveLabel(v, pLevel->addrBrk); - if (pLevel->addrSkip) { - sqlite3VdbeGoto(v, pLevel->addrSkip); - VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName)); - sqlite3VdbeJumpHere(v, pLevel->addrSkip); - sqlite3VdbeJumpHere(v, pLevel->addrSkip-2); - } -#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS - if (pLevel->addrLikeRep) { - sqlite3VdbeAddOp2(v, OP_DecrJumpZero, (int)(pLevel->iLikeRepCntr>>1), - pLevel->addrLikeRep); - VdbeCoverage(v); - } -#endif - if (pLevel->iLeftJoin) { - int ws = pLoop->wsFlags; - addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); - assert((ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); - if ((ws & WHERE_IDX_ONLY)==0) { - assert( pLevel->iTabCur==pTabList->a[pLevel->iFrom].iCursor ); - sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); - } - if ((ws & WHERE_INDEXED) - || ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx) - ) { - sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur); - } - if (pLevel->op==OP_Return) { - sqlite3VdbeAddOp2(v, OP_Gosub, pLevel->p1, pLevel->addrFirst); - } else { - sqlite3VdbeGoto(v, pLevel->addrFirst); - } - sqlite3VdbeJumpHere(v, addr); - } - VdbeModuleComment((v, "End WHERE-loop%d: %s", i, - pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); - } - - /* The "break" point is here, just past the end of the outer loop. - ** Set it. - */ - sqlite3VdbeResolveLabel(v, pWInfo->iBreak); - - assert( pWInfo->nLevel<=pTabList->nSrc ); - for (i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++) { - int k, last; - VdbeOp *pOp; - Index *pIdx = 0; - struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; - Table *pTab = pTabItem->pTab; - assert( pTab!=0 ); - pLoop = pLevel->pWLoop; - - /* For a co-routine, change all OP_Column references to the table of - ** the co-routine into OP_Copy of result contained in a register. - ** OP_Rowid becomes OP_Null. - */ - if (pTabItem->fg.viaCoroutine) { - testcase( pParse->db->mallocFailed ); - translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, - pTabItem->regResult, 0); - continue; - } - -#ifdef SQLITE_ENABLE_EARLY_CURSOR_CLOSE - /* Close all of the cursors that were opened by sqlite3WhereBegin. - ** Except, do not close cursors that will be reused by the OR optimization - ** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors - ** created for the ONEPASS optimization. - */ - if ((pTab->tabFlags & TF_Ephemeral)==0 - && pTab->pSelect==0 - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 - ) { - int ws = pLoop->wsFlags; - if (pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0) { - sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); - } - if ((ws & WHERE_INDEXED)!=0 - && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 - && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1] - ) { - sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); - } - } -#endif - - /* If this scan uses an index, make VDBE code substitutions to read data - ** from the index instead of from the table where possible. In some cases - ** this optimization prevents the table from ever being read, which can - ** yield a significant performance boost. - ** - ** Calls to the code generator in between sqlite3WhereBegin and - ** sqlite3WhereEnd will have created code that references the table - ** directly. This loop scans all that code looking for opcodes - ** that reference the table and converts them into opcodes that - ** reference the index. - */ - if (pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY)) { - pIdx = pLoop->u.btree.pIndex; - } else if (pLoop->wsFlags & WHERE_MULTI_OR) { - pIdx = pLevel->u.pCovidx; - } - if (pIdx - && (pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable)) - && !db->mallocFailed - ) { - last = sqlite3VdbeCurrentAddr(v); - k = pLevel->addrBody; -#ifdef SQLITE_DEBUG - if (db->flags & SQLITE_VdbeAddopTrace) { - printf("TRANSLATE opcodes in range %d..%d\n", k, last-1); - } -#endif - pOp = sqlite3VdbeGetOp(v, k); - for (; kp1!=pLevel->iTabCur) continue; - if (pOp->opcode==OP_Column -#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC - || pOp->opcode==OP_Offset -#endif - ) { - int x = pOp->p2; - assert( pIdx->pTable==pTab ); - if (!HasRowid(pTab)) { - Index *pPk = sqlite3PrimaryKeyIndex(pTab); - x = pPk->aiColumn[x]; - assert( x>=0 ); - } - x = sqlite3ColumnOfIndex(pIdx, x); - if (x>=0) { - pOp->p2 = x; - pOp->p1 = pLevel->iIdxCur; - OpcodeRewriteTrace(db, k, pOp); - } - assert((pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 - || pWInfo->eOnePass ); - } else if (pOp->opcode==OP_Rowid) { - pOp->p1 = pLevel->iIdxCur; - pOp->opcode = OP_IdxRowid; - OpcodeRewriteTrace(db, k, pOp); - } else if (pOp->opcode==OP_IfNullRow) { - pOp->p1 = pLevel->iIdxCur; - OpcodeRewriteTrace(db, k, pOp); - } - } -#ifdef SQLITE_DEBUG - if (db->flags & SQLITE_VdbeAddopTrace) printf("TRANSLATE complete\n"); -#endif - } - } - - /* Final cleanup - */ - pParse->nQueryLoop = pWInfo->savedNQueryLoop; - whereInfoFree(db, pWInfo); - return; -} diff --git a/test/bug-hunting/cve/CVE-2019-19334/cmd.txt b/test/bug-hunting/cve/CVE-2019-19334/cmd.txt deleted file mode 100644 index e993abff180..00000000000 --- a/test/bug-hunting/cve/CVE-2019-19334/cmd.txt +++ /dev/null @@ -1 +0,0 @@ --DLY_CHECK_ERR_RETURN(A,B,C)= diff --git a/test/bug-hunting/cve/CVE-2019-19334/expected.txt b/test/bug-hunting/cve/CVE-2019-19334/expected.txt deleted file mode 100644 index 8343bfd9193..00000000000 --- a/test/bug-hunting/cve/CVE-2019-19334/expected.txt +++ /dev/null @@ -1,3 +0,0 @@ -parser.c:1024:bughuntingBufferOverflow -parser.c:1026:bughuntingBufferOverflow - diff --git a/test/bug-hunting/cve/CVE-2019-19334/parser.c b/test/bug-hunting/cve/CVE-2019-19334/parser.c deleted file mode 100644 index c9ae171b4b2..00000000000 --- a/test/bug-hunting/cve/CVE-2019-19334/parser.c +++ /dev/null @@ -1,3941 +0,0 @@ -/** - * @file parser.c - * @author Radek Krejci - * @brief common libyang parsers routines implementations - * - * Copyright (c) 2015-2017 CESNET, z.s.p.o. - * - * This source code is licensed under BSD 3-Clause License (the "License"). - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "context.h" -#include "libyang.h" -#include "parser.h" -#include "resolve.h" -#include "tree_internal.h" -#include "parser_yang.h" -#include "xpath.h" - -#define LYP_URANGE_LEN 19 - -static char *lyp_ublock2urange[][2] = { - {"BasicLatin", "[\\x{0000}-\\x{007F}]"}, - {"Latin-1Supplement", "[\\x{0080}-\\x{00FF}]"}, - {"LatinExtended-A", "[\\x{0100}-\\x{017F}]"}, - {"LatinExtended-B", "[\\x{0180}-\\x{024F}]"}, - {"IPAExtensions", "[\\x{0250}-\\x{02AF}]"}, - {"SpacingModifierLetters", "[\\x{02B0}-\\x{02FF}]"}, - {"CombiningDiacriticalMarks", "[\\x{0300}-\\x{036F}]"}, - {"Greek", "[\\x{0370}-\\x{03FF}]"}, - {"Cyrillic", "[\\x{0400}-\\x{04FF}]"}, - {"Armenian", "[\\x{0530}-\\x{058F}]"}, - {"Hebrew", "[\\x{0590}-\\x{05FF}]"}, - {"Arabic", "[\\x{0600}-\\x{06FF}]"}, - {"Syriac", "[\\x{0700}-\\x{074F}]"}, - {"Thaana", "[\\x{0780}-\\x{07BF}]"}, - {"Devanagari", "[\\x{0900}-\\x{097F}]"}, - {"Bengali", "[\\x{0980}-\\x{09FF}]"}, - {"Gurmukhi", "[\\x{0A00}-\\x{0A7F}]"}, - {"Gujarati", "[\\x{0A80}-\\x{0AFF}]"}, - {"Oriya", "[\\x{0B00}-\\x{0B7F}]"}, - {"Tamil", "[\\x{0B80}-\\x{0BFF}]"}, - {"Telugu", "[\\x{0C00}-\\x{0C7F}]"}, - {"Kannada", "[\\x{0C80}-\\x{0CFF}]"}, - {"Malayalam", "[\\x{0D00}-\\x{0D7F}]"}, - {"Sinhala", "[\\x{0D80}-\\x{0DFF}]"}, - {"Thai", "[\\x{0E00}-\\x{0E7F}]"}, - {"Lao", "[\\x{0E80}-\\x{0EFF}]"}, - {"Tibetan", "[\\x{0F00}-\\x{0FFF}]"}, - {"Myanmar", "[\\x{1000}-\\x{109F}]"}, - {"Georgian", "[\\x{10A0}-\\x{10FF}]"}, - {"HangulJamo", "[\\x{1100}-\\x{11FF}]"}, - {"Ethiopic", "[\\x{1200}-\\x{137F}]"}, - {"Cherokee", "[\\x{13A0}-\\x{13FF}]"}, - {"UnifiedCanadianAboriginalSyllabics", "[\\x{1400}-\\x{167F}]"}, - {"Ogham", "[\\x{1680}-\\x{169F}]"}, - {"Runic", "[\\x{16A0}-\\x{16FF}]"}, - {"Khmer", "[\\x{1780}-\\x{17FF}]"}, - {"Mongolian", "[\\x{1800}-\\x{18AF}]"}, - {"LatinExtendedAdditional", "[\\x{1E00}-\\x{1EFF}]"}, - {"GreekExtended", "[\\x{1F00}-\\x{1FFF}]"}, - {"GeneralPunctuation", "[\\x{2000}-\\x{206F}]"}, - {"SuperscriptsandSubscripts", "[\\x{2070}-\\x{209F}]"}, - {"CurrencySymbols", "[\\x{20A0}-\\x{20CF}]"}, - {"CombiningMarksforSymbols", "[\\x{20D0}-\\x{20FF}]"}, - {"LetterlikeSymbols", "[\\x{2100}-\\x{214F}]"}, - {"NumberForms", "[\\x{2150}-\\x{218F}]"}, - {"Arrows", "[\\x{2190}-\\x{21FF}]"}, - {"MathematicalOperators", "[\\x{2200}-\\x{22FF}]"}, - {"MiscellaneousTechnical", "[\\x{2300}-\\x{23FF}]"}, - {"ControlPictures", "[\\x{2400}-\\x{243F}]"}, - {"OpticalCharacterRecognition", "[\\x{2440}-\\x{245F}]"}, - {"EnclosedAlphanumerics", "[\\x{2460}-\\x{24FF}]"}, - {"BoxDrawing", "[\\x{2500}-\\x{257F}]"}, - {"BlockElements", "[\\x{2580}-\\x{259F}]"}, - {"GeometricShapes", "[\\x{25A0}-\\x{25FF}]"}, - {"MiscellaneousSymbols", "[\\x{2600}-\\x{26FF}]"}, - {"Dingbats", "[\\x{2700}-\\x{27BF}]"}, - {"BraillePatterns", "[\\x{2800}-\\x{28FF}]"}, - {"CJKRadicalsSupplement", "[\\x{2E80}-\\x{2EFF}]"}, - {"KangxiRadicals", "[\\x{2F00}-\\x{2FDF}]"}, - {"IdeographicDescriptionCharacters", "[\\x{2FF0}-\\x{2FFF}]"}, - {"CJKSymbolsandPunctuation", "[\\x{3000}-\\x{303F}]"}, - {"Hiragana", "[\\x{3040}-\\x{309F}]"}, - {"Katakana", "[\\x{30A0}-\\x{30FF}]"}, - {"Bopomofo", "[\\x{3100}-\\x{312F}]"}, - {"HangulCompatibilityJamo", "[\\x{3130}-\\x{318F}]"}, - {"Kanbun", "[\\x{3190}-\\x{319F}]"}, - {"BopomofoExtended", "[\\x{31A0}-\\x{31BF}]"}, - {"EnclosedCJKLettersandMonths", "[\\x{3200}-\\x{32FF}]"}, - {"CJKCompatibility", "[\\x{3300}-\\x{33FF}]"}, - {"CJKUnifiedIdeographsExtensionA", "[\\x{3400}-\\x{4DB5}]"}, - {"CJKUnifiedIdeographs", "[\\x{4E00}-\\x{9FFF}]"}, - {"YiSyllables", "[\\x{A000}-\\x{A48F}]"}, - {"YiRadicals", "[\\x{A490}-\\x{A4CF}]"}, - {"HangulSyllables", "[\\x{AC00}-\\x{D7A3}]"}, - {"PrivateUse", "[\\x{E000}-\\x{F8FF}]"}, - {"CJKCompatibilityIdeographs", "[\\x{F900}-\\x{FAFF}]"}, - {"AlphabeticPresentationForms", "[\\x{FB00}-\\x{FB4F}]"}, - {"ArabicPresentationForms-A", "[\\x{FB50}-\\x{FDFF}]"}, - {"CombiningHalfMarks", "[\\x{FE20}-\\x{FE2F}]"}, - {"CJKCompatibilityForms", "[\\x{FE30}-\\x{FE4F}]"}, - {"SmallFormVariants", "[\\x{FE50}-\\x{FE6F}]"}, - {"ArabicPresentationForms-B", "[\\x{FE70}-\\x{FEFE}]"}, - {"HalfwidthandFullwidthForms", "[\\x{FF00}-\\x{FFEF}]"}, - {NULL, NULL} -}; - -const char *ly_stmt_str[] = { - [LY_STMT_UNKNOWN] = "", - [LY_STMT_ARGUMENT] = "argument", - [LY_STMT_BASE] = "base", - [LY_STMT_BELONGSTO] = "belongs-to", - [LY_STMT_CONTACT] = "contact", - [LY_STMT_DEFAULT] = "default", - [LY_STMT_DESCRIPTION] = "description", - [LY_STMT_ERRTAG] = "error-app-tag", - [LY_STMT_ERRMSG] = "error-message", - [LY_STMT_KEY] = "key", - [LY_STMT_NAMESPACE] = "namespace", - [LY_STMT_ORGANIZATION] = "organization", - [LY_STMT_PATH] = "path", - [LY_STMT_PREFIX] = "prefix", - [LY_STMT_PRESENCE] = "presence", - [LY_STMT_REFERENCE] = "reference", - [LY_STMT_REVISIONDATE] = "revision-date", - [LY_STMT_UNITS] = "units", - [LY_STMT_VALUE] = "value", - [LY_STMT_VERSION] = "yang-version", - [LY_STMT_MODIFIER] = "modifier", - [LY_STMT_REQINSTANCE] = "require-instance", - [LY_STMT_YINELEM] = "yin-element", - [LY_STMT_CONFIG] = "config", - [LY_STMT_MANDATORY] = "mandatory", - [LY_STMT_ORDEREDBY] = "ordered-by", - [LY_STMT_STATUS] = "status", - [LY_STMT_DIGITS] = "fraction-digits", - [LY_STMT_MAX] = "max-elements", - [LY_STMT_MIN] = "min-elements", - [LY_STMT_POSITION] = "position", - [LY_STMT_UNIQUE] = "unique", - [LY_STMT_MODULE] = "module", - [LY_STMT_SUBMODULE] = "submodule", - [LY_STMT_ACTION] = "action", - [LY_STMT_ANYDATA] = "anydata", - [LY_STMT_ANYXML] = "anyxml", - [LY_STMT_CASE] = "case", - [LY_STMT_CHOICE] = "choice", - [LY_STMT_CONTAINER] = "container", - [LY_STMT_GROUPING] = "grouping", - [LY_STMT_INPUT] = "input", - [LY_STMT_LEAF] = "leaf", - [LY_STMT_LEAFLIST] = "leaf-list", - [LY_STMT_LIST] = "list", - [LY_STMT_NOTIFICATION] = "notification", - [LY_STMT_OUTPUT] = "output", - [LY_STMT_RPC] = "rpc", - [LY_STMT_USES] = "uses", - [LY_STMT_TYPEDEF] = "typedef", - [LY_STMT_TYPE] = "type", - [LY_STMT_BIT] = "bit", - [LY_STMT_ENUM] = "enum", - [LY_STMT_REFINE] = "refine", - [LY_STMT_AUGMENT] = "augment", - [LY_STMT_DEVIATE] = "deviate", - [LY_STMT_DEVIATION] = "deviation", - [LY_STMT_EXTENSION] = "extension", - [LY_STMT_FEATURE] = "feature", - [LY_STMT_IDENTITY] = "identity", - [LY_STMT_IFFEATURE] = "if-feature", - [LY_STMT_IMPORT] = "import", - [LY_STMT_INCLUDE] = "include", - [LY_STMT_LENGTH] = "length", - [LY_STMT_MUST] = "must", - [LY_STMT_PATTERN] = "pattern", - [LY_STMT_RANGE] = "range", - [LY_STMT_WHEN] = "when", - [LY_STMT_REVISION] = "revision" -}; - -int -lyp_is_rpc_action(struct lys_node *node) -{ - assert(node); - - while (lys_parent(node)) { - node = lys_parent(node); - if (node->nodetype == LYS_ACTION) { - break; - } - } - - if (node->nodetype & (LYS_RPC | LYS_ACTION)) { - return 1; - } else { - return 0; - } -} - -int -lyp_data_check_options(struct ly_ctx *ctx, int options, const char *func) -{ - int x = options & LYD_OPT_TYPEMASK; - - /* LYD_OPT_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG */ - if (options & LYD_OPT_WHENAUTODEL) { - if ((x == LYD_OPT_EDIT) || (x == LYD_OPT_NOTIF_FILTER)) { - LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG)", - func, options); - return 1; - } - } - - if (options & (LYD_OPT_DATA_ADD_YANGLIB | LYD_OPT_DATA_NO_YANGLIB)) { - if (x != LYD_OPT_DATA) { - LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_*_YANGLIB can be used only with LYD_OPT_DATA)", - func, options); - return 1; - } - } - - /* "is power of 2" algorithm, with 0 exception */ - if (x && !(x && !(x & (x - 1)))) { - LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (multiple data type flags set).", func, options); - return 1; - } - - return 0; -} - -int -lyp_mmap(struct ly_ctx *ctx, int fd, size_t addsize, size_t *length, void **addr) -{ - struct stat sb; - long pagesize; - size_t m; - - assert(fd >= 0); - if (fstat(fd, &sb) == -1) { - LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno)); - return 1; - } - if (!S_ISREG(sb.st_mode)) { - LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file."); - return 1; - } - if (!sb.st_size) { - *addr = NULL; - return 0; - } - pagesize = sysconf(_SC_PAGESIZE); - ++addsize; /* at least one additional byte for terminating NULL byte */ - - m = sb.st_size % pagesize; - if (m && pagesize - m >= addsize) { - /* there will be enough space after the file content mapping to provide zeroed additional bytes */ - *length = sb.st_size + addsize; - *addr = mmap(NULL, *length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - } else { - /* there will not be enough bytes after the file content mapping for the additional bytes and some of them - * would overflow into another page that would not be zeroed and any access into it would generate SIGBUS. - * Therefore we have to do the following hack with double mapping. First, the required number of bytes - * (including the additional bytes) is required as anonymous and thus they will be really provided (actually more - * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address - * where the anonymous mapping starts. */ - *length = sb.st_size + pagesize; - *addr = mmap(NULL, *length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - *addr = mmap(*addr, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0); - } - if (*addr == MAP_FAILED) { - LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno)); - return 1; - } - - return 0; -} - -int -lyp_munmap(void *addr, size_t length) -{ - return munmap(addr, length); -} - -int -lyp_add_ietf_netconf_annotations_config(struct lys_module *mod) -{ - void *reallocated; - struct lys_ext_instance_complex *op; - struct lys_type **type; - struct lys_node_anydata *anyxml; - int i; - struct ly_ctx *ctx = mod->ctx; /* shortcut */ - - reallocated = realloc(mod->ext, (mod->ext_size + 3) * sizeof *mod->ext); - LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx), EXIT_FAILURE); - mod->ext = reallocated; - /* 1) edit-config's operation */ - op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); - LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); - mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; - op->arg_value = lydict_insert(ctx, "operation", 9); - op->def = &ctx->models.list[0]->extensions[0]; - op->ext_type = LYEXT_COMPLEX; - op->module = op->parent = mod; - op->parent_type = LYEXT_PAR_MODULE; - op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; - op->nodetype = LYS_EXT; - type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ - *type = calloc(1, sizeof(struct lys_type)); - LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); - (*type)->base = LY_TYPE_ENUM; - (*type)->der = ly_types[LY_TYPE_ENUM]; - (*type)->parent = (struct lys_tpdf *)op; - (*type)->info.enums.count = 5; - (*type)->info.enums.enm = calloc(5, sizeof *(*type)->info.enums.enm); - LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm, LOGMEM(ctx), EXIT_FAILURE); - (*type)->info.enums.enm[0].value = 0; - (*type)->info.enums.enm[0].name = lydict_insert(ctx, "merge", 5); - (*type)->info.enums.enm[1].value = 1; - (*type)->info.enums.enm[1].name = lydict_insert(ctx, "replace", 7); - (*type)->info.enums.enm[2].value = 2; - (*type)->info.enums.enm[2].name = lydict_insert(ctx, "create", 6); - (*type)->info.enums.enm[3].value = 3; - (*type)->info.enums.enm[3].name = lydict_insert(ctx, "delete", 6); - (*type)->info.enums.enm[4].value = 4; - (*type)->info.enums.enm[4].name = lydict_insert(ctx, "remove", 6); - mod->ext_size++; - - /* 2) filter's type */ - op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); - LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); - mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; - op->arg_value = lydict_insert(ctx, "type", 4); - op->def = &ctx->models.list[0]->extensions[0]; - op->ext_type = LYEXT_COMPLEX; - op->module = op->parent = mod; - op->parent_type = LYEXT_PAR_MODULE; - op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; - op->nodetype = LYS_EXT; - type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ - *type = calloc(1, sizeof(struct lys_type)); - LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); - (*type)->base = LY_TYPE_ENUM; - (*type)->der = ly_types[LY_TYPE_ENUM]; - (*type)->parent = (struct lys_tpdf *)op; - (*type)->info.enums.count = 2; - (*type)->info.enums.enm = calloc(2, sizeof *(*type)->info.enums.enm); - LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm, LOGMEM(ctx), EXIT_FAILURE); - (*type)->info.enums.enm[0].value = 0; - (*type)->info.enums.enm[0].name = lydict_insert(ctx, "subtree", 7); - (*type)->info.enums.enm[1].value = 1; - (*type)->info.enums.enm[1].name = lydict_insert(ctx, "xpath", 5); - for (i = mod->features_size; i > 0; i--) { - if (!strcmp(mod->features[i - 1].name, "xpath")) { - (*type)->info.enums.enm[1].iffeature_size = 1; - (*type)->info.enums.enm[1].iffeature = calloc(1, sizeof(struct lys_feature)); - LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature, LOGMEM(ctx), EXIT_FAILURE); - (*type)->info.enums.enm[1].iffeature[0].expr = malloc(sizeof(uint8_t)); - LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature[0].expr, LOGMEM(ctx), EXIT_FAILURE); - *(*type)->info.enums.enm[1].iffeature[0].expr = 3; /* LYS_IFF_F */ - (*type)->info.enums.enm[1].iffeature[0].features = malloc(sizeof(struct lys_feature*)); - LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature[0].features, LOGMEM(ctx), EXIT_FAILURE); - (*type)->info.enums.enm[1].iffeature[0].features[0] = &mod->features[i - 1]; - break; - } - } - mod->ext_size++; - - /* 3) filter's select */ - op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); - LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); - mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; - op->arg_value = lydict_insert(ctx, "select", 6); - op->def = &ctx->models.list[0]->extensions[0]; - op->ext_type = LYEXT_COMPLEX; - op->module = op->parent = mod; - op->parent_type = LYEXT_PAR_MODULE; - op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; - op->nodetype = LYS_EXT; - type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ - *type = calloc(1, sizeof(struct lys_type)); - LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); - (*type)->base = LY_TYPE_STRING; - (*type)->der = ly_types[LY_TYPE_STRING]; - (*type)->parent = (struct lys_tpdf *)op; - mod->ext_size++; - - /* 4) URL config */ - anyxml = calloc(1, sizeof *anyxml); - LY_CHECK_ERR_RETURN(!anyxml, LOGMEM(ctx), EXIT_FAILURE); - anyxml->nodetype = LYS_ANYXML; - anyxml->prev = (struct lys_node *)anyxml; - anyxml->name = lydict_insert(ctx, "config", 0); - anyxml->module = mod; - anyxml->flags = LYS_CONFIG_W; - if (lys_node_addchild(NULL, mod, (struct lys_node *)anyxml, 0)) { - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -/* logs directly - * base: 0 - to accept decimal, octal, hexadecimal (in default value) - * 10 - to accept only decimal (instance value) - */ -static int -parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret, struct lyd_node *node) -{ - char *strptr; - - assert(node); - - if (!val_str || !val_str[0]) { - goto error; - } - - /* convert to 64-bit integer, all the redundant characters are handled */ - errno = 0; - strptr = NULL; - - /* parse the value */ - *ret = strtoll(val_str, &strptr, base); - if (errno || (*ret < min) || (*ret > max)) { - goto error; - } else if (strptr && *strptr) { - while (isspace(*strptr)) { - ++strptr; - } - if (*strptr) { - goto error; - } - } - - return EXIT_SUCCESS; - -error: - LOGVAL(node->schema->module->ctx, LYE_INVAL, LY_VLOG_LYD, node, val_str ? val_str : "", node->schema->name); - return EXIT_FAILURE; -} - -/* logs directly - * base: 0 - to accept decimal, octal, hexadecimal (in default value) - * 10 - to accept only decimal (instance value) - */ -static int -parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret, struct lyd_node *node) -{ - char *strptr; - uint64_t u; - - assert(node); - - if (!val_str || !val_str[0]) { - goto error; - } - - errno = 0; - strptr = NULL; - u = strtoull(val_str, &strptr, base); - if (errno || (u > max)) { - goto error; - } else if (strptr && *strptr) { - while (isspace(*strptr)) { - ++strptr; - } - if (*strptr) { - goto error; - } - } else if (u != 0 && val_str[0] == '-') { - goto error; - } - - *ret = u; - return EXIT_SUCCESS; - -error: - LOGVAL(node->schema->module->ctx, LYE_INVAL, LY_VLOG_LYD, node, val_str ? val_str : "", node->schema->name); - return EXIT_FAILURE; -} - -/* logs directly - * - * kind == 0 - unsigned (unum used), 1 - signed (snum used), 2 - floating point (fnum used) - */ -static int -validate_length_range(uint8_t kind, uint64_t unum, int64_t snum, int64_t fnum, uint8_t fnum_dig, struct lys_type *type, - const char *val_str, struct lyd_node *node) -{ - struct lys_restr *restr = NULL; - struct len_ran_intv *intv = NULL, *tmp_intv; - struct lys_type *cur_type; - struct ly_ctx *ctx = type->parent->module->ctx; - int match; - - if (resolve_len_ran_interval(ctx, NULL, type, &intv)) { - /* already done during schema parsing */ - LOGINT(ctx); - return EXIT_FAILURE; - } - if (!intv) { - return EXIT_SUCCESS; - } - - /* I know that all intervals belonging to a single restriction share one type pointer */ - tmp_intv = intv; - cur_type = intv->type; - do { - match = 0; - for (; tmp_intv && (tmp_intv->type == cur_type); tmp_intv = tmp_intv->next) { - if (match) { - /* just iterate through the rest of this restriction intervals */ - continue; - } - - if (((kind == 0) && (unum < tmp_intv->value.uval.min)) - || ((kind == 1) && (snum < tmp_intv->value.sval.min)) - || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) < 0))) { - break; - } - - if (((kind == 0) && (unum >= tmp_intv->value.uval.min) && (unum <= tmp_intv->value.uval.max)) - || ((kind == 1) && (snum >= tmp_intv->value.sval.min) && (snum <= tmp_intv->value.sval.max)) - || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) > -1) - && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.max, cur_type->info.dec64.dig) < 1))) { - match = 1; - } - } - - if (!match) { - break; - } else if (tmp_intv) { - cur_type = tmp_intv->type; - } - } while (tmp_intv); - - while (intv) { - tmp_intv = intv->next; - free(intv); - intv = tmp_intv; - } - - if (!match) { - switch (cur_type->base) { - case LY_TYPE_BINARY: - restr = cur_type->info.binary.length; - break; - case LY_TYPE_DEC64: - restr = cur_type->info.dec64.range; - break; - case LY_TYPE_INT8: - case LY_TYPE_INT16: - case LY_TYPE_INT32: - case LY_TYPE_INT64: - case LY_TYPE_UINT8: - case LY_TYPE_UINT16: - case LY_TYPE_UINT32: - case LY_TYPE_UINT64: - restr = cur_type->info.num.range; - break; - case LY_TYPE_STRING: - restr = cur_type->info.str.length; - break; - default: - LOGINT(ctx); - return EXIT_FAILURE; - } - - LOGVAL(ctx, LYE_NOCONSTR, LY_VLOG_LYD, node, (val_str ? val_str : ""), restr ? restr->expr : ""); - if (restr && restr->emsg) { - ly_vlog_str(ctx, LY_VLOG_PREV, restr->emsg); - } - if (restr && restr->eapptag) { - ly_err_last_set_apptag(ctx, restr->eapptag); - } - return EXIT_FAILURE; - } - return EXIT_SUCCESS; -} - -/* logs directly */ -static int -validate_pattern(struct ly_ctx *ctx, const char *val_str, struct lys_type *type, struct lyd_node *node) -{ - int rc; - unsigned int i; -#ifndef LY_ENABLED_CACHE - pcre *precomp; -#endif - - assert(ctx && (type->base == LY_TYPE_STRING)); - - if (!val_str) { - val_str = ""; - } - - if (type->der && validate_pattern(ctx, val_str, &type->der->type, node)) { - return EXIT_FAILURE; - } - -#ifdef LY_ENABLED_CACHE - /* there is no cache, build it */ - if (!type->info.str.patterns_pcre && type->info.str.pat_count) { - type->info.str.patterns_pcre = malloc(2 * type->info.str.pat_count * sizeof *type->info.str.patterns_pcre); - LY_CHECK_ERR_RETURN(!type->info.str.patterns_pcre, LOGMEM(ctx), -1); - - for (i = 0; i < type->info.str.pat_count; ++i) { - if (lyp_precompile_pattern(ctx, &type->info.str.patterns[i].expr[1], - (pcre**)&type->info.str.patterns_pcre[i * 2], - (pcre_extra**)&type->info.str.patterns_pcre[i * 2 + 1])) { - return EXIT_FAILURE; - } - } - } -#endif - - for (i = 0; i < type->info.str.pat_count; ++i) { -#ifdef LY_ENABLED_CACHE - rc = pcre_exec((pcre *)type->info.str.patterns_pcre[2 * i], (pcre_extra *)type->info.str.patterns_pcre[2 * i + 1], - val_str, strlen(val_str), 0, 0, NULL, 0); -#else - if (lyp_check_pattern(ctx, &type->info.str.patterns[i].expr[1], &precomp)) { - return EXIT_FAILURE; - } - rc = pcre_exec(precomp, NULL, val_str, strlen(val_str), 0, 0, NULL, 0); - free(precomp); -#endif - if ((rc && type->info.str.patterns[i].expr[0] == 0x06) || (!rc && type->info.str.patterns[i].expr[0] == 0x15)) { - LOGVAL(ctx, LYE_NOCONSTR, LY_VLOG_LYD, node, val_str, &type->info.str.patterns[i].expr[1]); - if (type->info.str.patterns[i].emsg) { - ly_vlog_str(ctx, LY_VLOG_PREV, type->info.str.patterns[i].emsg); - } - if (type->info.str.patterns[i].eapptag) { - ly_err_last_set_apptag(ctx, type->info.str.patterns[i].eapptag); - } - return EXIT_FAILURE; - } - } - - return EXIT_SUCCESS; -} - -static void -check_number(const char *str_num, const char **num_end, LY_DATA_TYPE base) -{ - if (!isdigit(str_num[0]) && (str_num[0] != '-') && (str_num[0] != '+')) { - *num_end = str_num; - return; - } - - if ((str_num[0] == '-') || (str_num[0] == '+')) { - ++str_num; - } - - while (isdigit(str_num[0])) { - ++str_num; - } - - if ((base != LY_TYPE_DEC64) || (str_num[0] != '.') || !isdigit(str_num[1])) { - *num_end = str_num; - return; - } - - ++str_num; - while (isdigit(str_num[0])) { - ++str_num; - } - - *num_end = str_num; -} - -/** - * @brief Checks the syntax of length or range statement, - * on success checks the semantics as well. Does not log. - * - * @param[in] expr Length or range expression. - * @param[in] type Type with the restriction. - * - * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. - */ -int -lyp_check_length_range(struct ly_ctx *ctx, const char *expr, struct lys_type *type) -{ - struct len_ran_intv *intv = NULL, *tmp_intv; - const char *c = expr, *tail; - int ret = EXIT_FAILURE, flg = 1; /* first run flag */ - - assert(expr); - -lengthpart: - - while (isspace(*c)) { - c++; - } - - /* lower boundary or explicit number */ - if (!strncmp(c, "max", 3)) { -max: - c += 3; - while (isspace(*c)) { - c++; - } - if (*c != '\0') { - goto error; - } - - goto syntax_ok; - - } else if (!strncmp(c, "min", 3)) { - if (!flg) { - /* min cannot be used elsewhere than in the first length-part */ - goto error; - } else { - flg = 0; - } - c += 3; - while (isspace(*c)) { - c++; - } - - if (*c == '|') { - c++; - /* process next length-part */ - goto lengthpart; - } else if (*c == '\0') { - goto syntax_ok; - } else if (!strncmp(c, "..", 2)) { -upper: - c += 2; - while (isspace(*c)) { - c++; - } - if (*c == '\0') { - goto error; - } - - /* upper boundary */ - if (!strncmp(c, "max", 3)) { - goto max; - } - - check_number(c, &tail, type->base); - if (c == tail) { - goto error; - } - c = tail; - while (isspace(*c)) { - c++; - } - if (*c == '\0') { - goto syntax_ok; - } else if (*c == '|') { - c++; - /* process next length-part */ - goto lengthpart; - } else { - goto error; - } - } else { - goto error; - } - - } else if (isdigit(*c) || (*c == '-') || (*c == '+')) { - /* number */ - check_number(c, &tail, type->base); - if (c == tail) { - goto error; - } - c = tail; - - while (isspace(*c)) { - c++; - } - - if (*c == '|') { - c++; - /* process next length-part */ - goto lengthpart; - } else if (*c == '\0') { - goto syntax_ok; - } else if (!strncmp(c, "..", 2)) { - goto upper; - } - - } else { - goto error; - } - -syntax_ok: - if (resolve_len_ran_interval(ctx, expr, type, &intv)) { - goto error; - } - - ret = EXIT_SUCCESS; - -error: - while (intv) { - tmp_intv = intv->next; - free(intv); - intv = tmp_intv; - } - - return ret; -} - -/** - * @brief Checks pattern syntax. Logs directly. - * - * @param[in] pattern Pattern to check. - * @param[out] pcre_precomp Precompiled PCRE pattern. Can be NULL. - * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. - */ -int -lyp_check_pattern(struct ly_ctx *ctx, const char *pattern, pcre **pcre_precomp) -{ - int idx, idx2, start, end, err_offset, count; - char *perl_regex, *ptr; - const char *err_msg, *orig_ptr; - pcre *precomp; - - /* - * adjust the expression to a Perl equivalent - * - * http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs - */ - - /* we need to replace all "$" with "\$", count them now */ - for (count = 0, ptr = strchr(pattern, '$'); ptr; ++count, ptr = strchr(ptr + 1, '$')); - - perl_regex = malloc((strlen(pattern) + 4 + count) * sizeof(char)); - LY_CHECK_ERR_RETURN(!perl_regex, LOGMEM(ctx), EXIT_FAILURE); - perl_regex[0] = '\0'; - - ptr = perl_regex; - - if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) { - /* we wil add line-end anchoring */ - ptr[0] = '('; - ++ptr; - } - - for (orig_ptr = pattern; orig_ptr[0]; ++orig_ptr) { - if (orig_ptr[0] == '$') { - ptr += sprintf(ptr, "\\$"); - } else { - ptr[0] = orig_ptr[0]; - ++ptr; - } - } - - if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) { - ptr += sprintf(ptr, ")$"); - } else { - ptr[0] = '\0'; - ++ptr; - } - - /* substitute Unicode Character Blocks with exact Character Ranges */ - while ((ptr = strstr(perl_regex, "\\p{Is"))) { - start = ptr - perl_regex; - - ptr = strchr(ptr, '}'); - if (!ptr) { - LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 2, "unterminated character property"); - free(perl_regex); - return EXIT_FAILURE; - } - - end = (ptr - perl_regex) + 1; - - /* need more space */ - if (end - start < LYP_URANGE_LEN) { - perl_regex = ly_realloc(perl_regex, strlen(perl_regex) + (LYP_URANGE_LEN - (end - start)) + 1); - LY_CHECK_ERR_RETURN(!perl_regex, LOGMEM(ctx); free(perl_regex), EXIT_FAILURE); - } - - /* find our range */ - for (idx = 0; lyp_ublock2urange[idx][0]; ++idx) { - if (!strncmp(perl_regex + start + 5, lyp_ublock2urange[idx][0], strlen(lyp_ublock2urange[idx][0]))) { - break; - } - } - if (!lyp_ublock2urange[idx][0]) { - LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 5, "unknown block name"); - free(perl_regex); - return EXIT_FAILURE; - } - - /* make the space in the string and replace the block (but we cannot include brackets if it was already enclosed in them) */ - for (idx2 = 0, count = 0; idx2 < start; ++idx2) { - if ((perl_regex[idx2] == '[') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) { - ++count; - } - if ((perl_regex[idx2] == ']') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) { - --count; - } - } - if (count) { - /* skip brackets */ - memmove(perl_regex + start + (LYP_URANGE_LEN - 2), perl_regex + end, strlen(perl_regex + end) + 1); - memcpy(perl_regex + start, lyp_ublock2urange[idx][1] + 1, LYP_URANGE_LEN - 2); - } else { - memmove(perl_regex + start + LYP_URANGE_LEN, perl_regex + end, strlen(perl_regex + end) + 1); - memcpy(perl_regex + start, lyp_ublock2urange[idx][1], LYP_URANGE_LEN); - } - } - - /* must return 0, already checked during parsing */ - precomp = pcre_compile(perl_regex, PCRE_ANCHORED | PCRE_DOLLAR_ENDONLY | PCRE_NO_AUTO_CAPTURE, - &err_msg, &err_offset, NULL); - if (!precomp) { - LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + err_offset, err_msg); - free(perl_regex); - return EXIT_FAILURE; - } - free(perl_regex); - - if (pcre_precomp) { - *pcre_precomp = precomp; - } else { - free(precomp); - } - - return EXIT_SUCCESS; -} - -int -lyp_precompile_pattern(struct ly_ctx *ctx, const char *pattern, pcre** pcre_cmp, pcre_extra **pcre_std) -{ - const char *err_msg = NULL; - - if (lyp_check_pattern(ctx, pattern, pcre_cmp)) { - return EXIT_FAILURE; - } - - if (pcre_std && pcre_cmp) { - (*pcre_std) = pcre_study(*pcre_cmp, 0, &err_msg); - if (err_msg) { - LOGWRN(ctx, "Studying pattern \"%s\" failed (%s).", pattern, err_msg); - } - } - - return EXIT_SUCCESS; -} - -/** - * @brief Change the value into its canonical form. In libyang, additionally to the RFC, - * all identities have their module as a prefix in their canonical form. - * - * @param[in] ctx - * @param[in] type Type of the value. - * @param[in,out] value Original and then canonical value. - * @param[in] data1 If \p type is #LY_TYPE_BITS: (struct lys_type_bit **) type bit field, - * #LY_TYPE_DEC64: (int64_t *) parsed digits of the number itself without floating point, - * #LY_TYPE_IDENT: (const char *) local module name (identityref node module), - * #LY_TYPE_INT*: (int64_t *) parsed int number itself, - * #LY_TYPE_UINT*: (uint64_t *) parsed uint number itself, - * otherwise ignored. - * @param[in] data2 If \p type is #LY_TYPE_BITS: (int *) type bit field length, - * #LY_TYPE_DEC64: (uint8_t *) number of fraction digits (position of the floating point), - * otherwise ignored. - * @return 1 if a conversion took place, 0 if the value was kept the same, -1 on error. - */ -static int -make_canonical(struct ly_ctx *ctx, int type, const char **value, void *data1, void *data2) -{ - const uint16_t buf_len = 511; - char buf[buf_len + 1]; - struct lys_type_bit **bits = NULL; - struct lyxp_expr *exp; - const char *module_name, *cur_expr, *end; - int i, j, count; - int64_t num; - uint64_t unum; - uint8_t c; - -#define LOGBUF(str) LOGERR(ctx, LY_EINVAL, "Value \"%s\" is too long.", str) - - switch (type) { - case LY_TYPE_BITS: - bits = (struct lys_type_bit **)data1; - count = *((int *)data2); - /* in canonical form, the bits are ordered by their position */ - buf[0] = '\0'; - for (i = 0; i < count; i++) { - if (!bits[i]) { - /* bit not set */ - continue; - } - if (buf[0]) { - LY_CHECK_ERR_RETURN(strlen(buf) + 1 + strlen(bits[i]->name) > buf_len, LOGBUF(bits[i]->name), -1); - sprintf(buf + strlen(buf), " %s", bits[i]->name); - } else { - LY_CHECK_ERR_RETURN(strlen(bits[i]->name) > buf_len, LOGBUF(bits[i]->name), -1); - strcpy(buf, bits[i]->name); - } - } - break; - - case LY_TYPE_IDENT: - module_name = (const char *)data1; - /* identity must always have a prefix */ - if (!strchr(*value, ':')) { - sprintf(buf, "%s:%s", module_name, *value); - } else { - strcpy(buf, *value); - } - break; - - case LY_TYPE_INST: - exp = lyxp_parse_expr(ctx, *value); - LY_CHECK_ERR_RETURN(!exp, LOGINT(ctx), -1); - - module_name = NULL; - count = 0; - for (i = 0; (unsigned)i < exp->used; ++i) { - cur_expr = &exp->expr[exp->expr_pos[i]]; - - /* copy WS */ - if (i && ((end = exp->expr + exp->expr_pos[i - 1] + exp->tok_len[i - 1]) != cur_expr)) { - if (count + (cur_expr - end) > buf_len) { - lyxp_expr_free(exp); - LOGBUF(end); - return -1; - } - strncpy(&buf[count], end, cur_expr - end); - count += cur_expr - end; - } - - if ((exp->tokens[i] == LYXP_TOKEN_NAMETEST) && (end = strnchr(cur_expr, ':', exp->tok_len[i]))) { - /* get the module name with ":" */ - ++end; - j = end - cur_expr; - - if (!module_name || strncmp(cur_expr, module_name, j)) { - /* print module name with colon, it does not equal to the parent one */ - if (count + j > buf_len) { - lyxp_expr_free(exp); - LOGBUF(cur_expr); - return -1; - } - strncpy(&buf[count], cur_expr, j); - count += j; - } - module_name = cur_expr; - - /* copy the rest */ - if (count + (exp->tok_len[i] - j) > buf_len) { - lyxp_expr_free(exp); - LOGBUF(end); - return -1; - } - strncpy(&buf[count], end, exp->tok_len[i] - j); - count += exp->tok_len[i] - j; - } else { - if (count + exp->tok_len[i] > buf_len) { - lyxp_expr_free(exp); - LOGBUF(&exp->expr[exp->expr_pos[i]]); - return -1; - } - strncpy(&buf[count], &exp->expr[exp->expr_pos[i]], exp->tok_len[i]); - count += exp->tok_len[i]; - } - } - if (count > buf_len) { - LOGINT(ctx); - lyxp_expr_free(exp); - return -1; - } - buf[count] = '\0'; - - lyxp_expr_free(exp); - break; - - case LY_TYPE_DEC64: - num = *((int64_t *)data1); - c = *((uint8_t *)data2); - if (num) { - count = sprintf(buf, "%" PRId64 " ", num); - if ((num > 0 && (count - 1) <= c) - || (count - 2) <= c) { - /* we have 0. value, print the value with the leading zeros - * (one for 0. and also keep the correct with of num according - * to fraction-digits value) - * for (num<0) - extra character for '-' sign */ - count = sprintf(buf, "%0*" PRId64 " ", (num > 0) ? (c + 1) : (c + 2), num); - } - for (i = c, j = 1; i > 0; i--) { - if (j && i > 1 && buf[count - 2] == '0') { - /* we have trailing zero to skip */ - buf[count - 1] = '\0'; - } else { - j = 0; - buf[count - 1] = buf[count - 2]; - } - count--; - } - buf[count - 1] = '.'; - } else { - /* zero */ - sprintf(buf, "0.0"); - } - break; - - case LY_TYPE_INT8: - case LY_TYPE_INT16: - case LY_TYPE_INT32: - case LY_TYPE_INT64: - num = *((int64_t *)data1); - sprintf(buf, "%" PRId64, num); - break; - - case LY_TYPE_UINT8: - case LY_TYPE_UINT16: - case LY_TYPE_UINT32: - case LY_TYPE_UINT64: - unum = *((uint64_t *)data1); - sprintf(buf, "%" PRIu64, unum); - break; - - default: - /* should not be even called - just do nothing */ - return 0; - } - - if (strcmp(buf, *value)) { - lydict_remove(ctx, *value); - *value = lydict_insert(ctx, buf, 0); - return 1; - } - - return 0; - -#undef LOGBUF -} - -static const char * -ident_val_add_module_prefix(const char *value, const struct lyxml_elem *xml, struct ly_ctx *ctx) -{ - const struct lyxml_ns *ns; - const struct lys_module *mod; - char *str; - - do { - LY_TREE_FOR((struct lyxml_ns *)xml->attr, ns) { - if ((ns->type == LYXML_ATTR_NS) && !ns->prefix) { - /* match */ - break; - } - } - if (!ns) { - xml = xml->parent; - } - } while (!ns && xml); - - if (!ns) { - /* no default namespace */ - LOGINT(ctx); - return NULL; - } - - /* find module */ - mod = ly_ctx_get_module_by_ns(ctx, ns->value, NULL, 1); - if (!mod) { - LOGINT(ctx); - return NULL; - } - - if (asprintf(&str, "%s:%s", mod->name, value) == -1) { - LOGMEM(ctx); - return NULL; - } - lydict_remove(ctx, value); - - return lydict_insert_zc(ctx, str); -} - -/* - * xml - optional for converting instance-identifier and identityref into JSON format - * leaf - mandatory to know the context (necessary e.g. for prefixes in idenitytref values) - * attr - alternative to leaf in case of parsing value in annotations (attributes) - * local_mod - optional if the local module dos not match the module of leaf/attr - * store - flag for union resolution - we do not want to store the result, we are just learning the type - * dflt - whether the value is a default value from the schema - * trusted - whether the value is trusted to be valid (but may not be canonical, so it is canonized) - */ -struct lys_type * -lyp_parse_value(struct lys_type *type, const char **value_, struct lyxml_elem *xml, - struct lyd_node_leaf_list *leaf, struct lyd_attr *attr, struct lys_module *local_mod, - int store, int dflt, int trusted) -{ - struct lys_type *ret = NULL, *t; - struct lys_tpdf *tpdf; - enum int_log_opts prev_ilo; - int c, len, found = 0; - unsigned int i, j; - int64_t num; - uint64_t unum, uind, u = 0; - const char *ptr, *value = *value_, *itemname, *old_val_str = NULL; - struct lys_type_bit **bits = NULL; - struct lys_ident *ident; - lyd_val *val, old_val; - LY_DATA_TYPE *val_type, old_val_type; - uint8_t *val_flags, old_val_flags; - struct lyd_node *contextnode; - struct ly_ctx *ctx = type->parent->module->ctx; - - assert(leaf || attr); - - if (leaf) { - assert(!attr); - if (!local_mod) { - local_mod = leaf->schema->module; - } - val = &leaf->value; - val_type = &leaf->value_type; - val_flags = &leaf->value_flags; - contextnode = (struct lyd_node *)leaf; - itemname = leaf->schema->name; - } else { - assert(!leaf); - if (!local_mod) { - local_mod = attr->annotation->module; - } - val = &attr->value; - val_type = &attr->value_type; - val_flags = &attr->value_flags; - contextnode = attr->parent; - itemname = attr->name; - } - - /* fully clear the value */ - if (store) { - old_val_str = lydict_insert(ctx, *value_, 0); - lyd_free_value(*val, *val_type, *val_flags, type, old_val_str, &old_val, &old_val_type, &old_val_flags); - *val_flags &= ~LY_VALUE_UNRES; - } - - switch (type->base) { - case LY_TYPE_BINARY: - /* get number of octets for length validation */ - unum = 0; - ptr = NULL; - if (value) { - /* silently skip leading/trailing whitespaces */ - for (uind = 0; isspace(value[uind]); ++uind); - ptr = &value[uind]; - u = strlen(ptr); - while (u && isspace(ptr[u - 1])) { - --u; - } - unum = u; - for (uind = 0; uind < u; ++uind) { - if (ptr[uind] == '\n') { - unum--; - } else if ((ptr[uind] < '/' && ptr[uind] != '+') || - (ptr[uind] > '9' && ptr[uind] < 'A') || - (ptr[uind] > 'Z' && ptr[uind] < 'a') || ptr[uind] > 'z') { - if (ptr[uind] == '=') { - /* padding */ - if (uind == u - 2 && ptr[uind + 1] == '=') { - found = 2; - uind++; - } else if (uind == u - 1) { - found = 1; - } - } - if (!found) { - /* error */ - LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYD, contextnode, ptr[uind], &ptr[uind]); - LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Invalid Base64 character."); - goto error; - } - } - } - } - - if (unum & 3) { - /* base64 length must be multiple of 4 chars */ - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); - } - LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Base64 encoded value length must be divisible by 4."); - goto error; - } - - /* length of the encoded string */ - len = ((unum / 4) * 3) - found; - if (!trusted && validate_length_range(0, len, 0, 0, 0, type, value, contextnode)) { - goto error; - } - - if (value && (ptr != value || ptr[u] != '\0')) { - /* update the changed value */ - ptr = lydict_insert(ctx, ptr, u); - lydict_remove(ctx, *value_); - *value_ = ptr; - } - - if (store) { - /* store the result */ - val->binary = value; - *val_type = LY_TYPE_BINARY; - } - break; - - case LY_TYPE_BITS: - /* locate bits structure with the bits definitions - * since YANG 1.1 allows restricted bits, it is the first - * bits type with some explicit bit specification */ - for (; !type->info.bits.count; type = &type->der->type); - - if (value || store) { - /* allocate the array of pointers to bits definition */ - bits = calloc(type->info.bits.count, sizeof *bits); - LY_CHECK_ERR_GOTO(!bits, LOGMEM(ctx), error); - } - - if (!value) { - /* no bits set */ - if (store) { - /* store empty array */ - val->bit = bits; - *val_type = LY_TYPE_BITS; - } - break; - } - - c = 0; - i = 0; - while (value[c]) { - /* skip leading whitespaces */ - while (isspace(value[c])) { - c++; - } - if (!value[c]) { - /* trailing white spaces */ - break; - } - - /* get the length of the bit identifier */ - for (len = 0; value[c] && !isspace(value[c]); c++, len++); - - /* go back to the beginning of the identifier */ - c = c - len; - - /* find bit definition, identifiers appear ordered by their position */ - for (found = i = 0; i < type->info.bits.count; i++) { - if (!strncmp(type->info.bits.bit[i].name, &value[c], len) && !type->info.bits.bit[i].name[len]) { - /* we have match, check if the value is enabled ... */ - for (j = 0; !trusted && (j < type->info.bits.bit[i].iffeature_size); j++) { - if (!resolve_iffeature(&type->info.bits.bit[i].iffeature[j])) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); - } - LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, - "Bit \"%s\" is disabled by its %d. if-feature condition.", - type->info.bits.bit[i].name, j + 1); - free(bits); - goto error; - } - } - /* check that the value was not already set */ - if (bits[i]) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); - } - LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Bit \"%s\" used multiple times.", - type->info.bits.bit[i].name); - free(bits); - goto error; - } - /* ... and then store the pointer */ - bits[i] = &type->info.bits.bit[i]; - - /* stop searching */ - found = 1; - break; - } - } - - if (!found) { - /* referenced bit value does not exist */ - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); - } - free(bits); - goto error; - } - c = c + len; - } - - if (make_canonical(ctx, LY_TYPE_BITS, value_, bits, &type->info.bits.count) == -1) { - free(bits); - goto error; - } - - if (store) { - /* store the result */ - val->bit = bits; - *val_type = LY_TYPE_BITS; - } else { - free(bits); - } - break; - - case LY_TYPE_BOOL: - if (value && !strcmp(value, "true")) { - if (store) { - val->bln = 1; - } - } else if (!value || strcmp(value, "false")) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value ? value : "", itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value ? value : ""); - } - goto error; - } else { - if (store) { - val->bln = 0; - } - } - - if (store) { - *val_type = LY_TYPE_BOOL; - } - break; - - case LY_TYPE_DEC64: - if (!value || !value[0]) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); - } - goto error; - } - - ptr = value; - if (parse_range_dec64(&ptr, type->info.dec64.dig, &num) || ptr[0]) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); - } - goto error; - } - - if (!trusted && validate_length_range(2, 0, 0, num, type->info.dec64.dig, type, value, contextnode)) { - goto error; - } - - if (make_canonical(ctx, LY_TYPE_DEC64, value_, &num, &type->info.dec64.dig) == -1) { - goto error; - } - - if (store) { - /* store the result */ - val->dec64 = num; - *val_type = LY_TYPE_DEC64; - } - break; - - case LY_TYPE_EMPTY: - if (value && value[0]) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); - } - goto error; - } - - if (store) { - *val_type = LY_TYPE_EMPTY; - } - break; - - case LY_TYPE_ENUM: - /* locate enums structure with the enumeration definitions, - * since YANG 1.1 allows restricted enums, it is the first - * enum type with some explicit enum specification */ - for (; !type->info.enums.count; type = &type->der->type); - - /* find matching enumeration value */ - for (i = found = 0; i < type->info.enums.count; i++) { - if (value && !strcmp(value, type->info.enums.enm[i].name)) { - /* we have match, check if the value is enabled ... */ - for (j = 0; !trusted && (j < type->info.enums.enm[i].iffeature_size); j++) { - if (!resolve_iffeature(&type->info.enums.enm[i].iffeature[j])) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); - } - LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Enum \"%s\" is disabled by its %d. if-feature condition.", - value, j + 1); - goto error; - } - } - /* ... and store pointer to the definition */ - if (store) { - val->enm = &type->info.enums.enm[i]; - *val_type = LY_TYPE_ENUM; - } - found = 1; - break; - } - } - - if (!found) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value ? value : "", itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value ? value : ""); - } - goto error; - } - break; - - case LY_TYPE_IDENT: - if (!value) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); - } - goto error; - } - - if (xml) { - ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); - /* first, convert value into the json format, silently */ - value = transform_xml2json(ctx, value, xml, 0, 0); - ly_ilo_restore(NULL, prev_ilo, NULL, 0); - if (!value) { - /* invalid identityref format */ - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); - } - goto error; - } - - /* the value has no prefix (default namespace), but the element's namespace has a prefix, find default namespace */ - if (!strchr(value, ':') && xml->ns->prefix) { - value = ident_val_add_module_prefix(value, xml, ctx); - if (!value) { - goto error; - } - } - } else if (dflt) { - ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); - /* the value actually uses module's prefixes instead of the module names as in JSON format, - * we have to convert it */ - value = transform_schema2json(local_mod, value); - ly_ilo_restore(NULL, prev_ilo, NULL, 0); - if (!value) { - /* invalid identityref format or it was already transformed, so ignore the error here */ - value = lydict_insert(ctx, *value_, 0); - } - } else { - value = lydict_insert(ctx, *value_, 0); - } - /* value is now in the dictionary, whether it differs from *value_ or not */ - - ident = resolve_identref(type, value, contextnode, local_mod, dflt); - if (!ident) { - lydict_remove(ctx, value); - goto error; - } else if (store) { - /* store the result */ - val->ident = ident; - *val_type = LY_TYPE_IDENT; - } - - /* the value is always changed and includes prefix */ - if (dflt) { - type->parent->flags |= LYS_DFLTJSON; - } - - if (make_canonical(ctx, LY_TYPE_IDENT, &value, (void*)lys_main_module(local_mod)->name, NULL) == -1) { - lydict_remove(ctx, value); - goto error; - } - - /* replace the old value with the new one (even if they may be the same) */ - lydict_remove(ctx, *value_); - *value_ = value; - break; - - case LY_TYPE_INST: - if (!value) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); - } - goto error; - } - - if (xml) { - ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); - /* first, convert value into the json format, silently */ - value = transform_xml2json(ctx, value, xml, 1, 1); - ly_ilo_restore(NULL, prev_ilo, NULL, 0); - if (!value) { - /* invalid instance-identifier format */ - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); - } - goto error; - } else if (ly_strequal(value, *value_, 1)) { - /* we have actually created the same expression (prefixes are the same as the module names) - * so we have just increased dictionary's refcount - fix it */ - lydict_remove(ctx, value); - } - } else if (dflt) { - /* turn logging off */ - ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); - - /* the value actually uses module's prefixes instead of the module names as in JSON format, - * we have to convert it */ - value = transform_schema2json(local_mod, value); - if (!value) { - /* invalid identityref format or it was already transformed, so ignore the error here */ - value = *value_; - } else if (ly_strequal(value, *value_, 1)) { - /* we have actually created the same expression (prefixes are the same as the module names) - * so we have just increased dictionary's refcount - fix it */ - lydict_remove(ctx, value); - } - /* turn logging back on */ - ly_ilo_restore(NULL, prev_ilo, NULL, 0); - } else { - if ((c = make_canonical(ctx, LY_TYPE_INST, &value, NULL, NULL))) { - if (c == -1) { - goto error; - } - - /* if a change occurred, value was removed from the dictionary so fix the pointers */ - *value_ = value; - } - } - - if (store) { - /* note that the data node is an unresolved instance-identifier */ - val->instance = NULL; - *val_type = LY_TYPE_INST; - *val_flags |= LY_VALUE_UNRES; - } - - if (!ly_strequal(value, *value_, 1)) { - /* update the changed value */ - lydict_remove(ctx, *value_); - *value_ = value; - - /* we have to remember the conversion into JSON format to be able to print it in correct form */ - if (dflt) { - type->parent->flags |= LYS_DFLTJSON; - } - } - break; - - case LY_TYPE_LEAFREF: - if (!value) { - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); - } - goto error; - } - - /* it is called not only to get the final type, but mainly to update value to canonical or JSON form - * if needed */ - t = lyp_parse_value(&type->info.lref.target->type, value_, xml, leaf, attr, NULL, store, dflt, trusted); - value = *value_; /* refresh possibly changed value */ - if (!t) { - /* already logged */ - goto error; - } - - if (store) { - /* make the note that the data node is an unresolved leafref (value union was already filled) */ - *val_flags |= LY_VALUE_UNRES; - } - - type = t; - break; - - case LY_TYPE_STRING: - if (!trusted && validate_length_range(0, (value ? ly_strlen_utf8(value) : 0), 0, 0, 0, type, value, contextnode)) { - goto error; - } - - if (!trusted && validate_pattern(ctx, value, type, contextnode)) { - goto error; - } - - /* special handling of ietf-yang-types xpath1.0 */ - for (tpdf = type->der; - tpdf->module && (strcmp(tpdf->name, "xpath1.0") || strcmp(tpdf->module->name, "ietf-yang-types")); - tpdf = tpdf->type.der); - if (tpdf->module && xml) { - /* convert value into the json format */ - value = transform_xml2json(ctx, value ? value : "", xml, 1, 1); - if (!value) { - /* invalid instance-identifier format */ - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); - } - goto error; - } - - if (!ly_strequal(value, *value_, 1)) { - /* update the changed value */ - lydict_remove(ctx, *value_); - *value_ = value; - } - } - - if (store) { - /* store the result */ - val->string = value; - *val_type = LY_TYPE_STRING; - } - break; - - case LY_TYPE_INT8: - if (parse_int(value, __INT64_C(-128), __INT64_C(127), dflt ? 0 : 10, &num, contextnode) - || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { - goto error; - } - - if (make_canonical(ctx, LY_TYPE_INT8, value_, &num, NULL) == -1) { - goto error; - } - - if (store) { - /* store the result */ - val->int8 = (int8_t)num; - *val_type = LY_TYPE_INT8; - } - break; - - case LY_TYPE_INT16: - if (parse_int(value, __INT64_C(-32768), __INT64_C(32767), dflt ? 0 : 10, &num, contextnode) - || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { - goto error; - } - - if (make_canonical(ctx, LY_TYPE_INT16, value_, &num, NULL) == -1) { - goto error; - } - - if (store) { - /* store the result */ - val->int16 = (int16_t)num; - *val_type = LY_TYPE_INT16; - } - break; - - case LY_TYPE_INT32: - if (parse_int(value, __INT64_C(-2147483648), __INT64_C(2147483647), dflt ? 0 : 10, &num, contextnode) - || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { - goto error; - } - - if (make_canonical(ctx, LY_TYPE_INT32, value_, &num, NULL) == -1) { - goto error; - } - - if (store) { - /* store the result */ - val->int32 = (int32_t)num; - *val_type = LY_TYPE_INT32; - } - break; - - case LY_TYPE_INT64: - if (parse_int(value, __INT64_C(-9223372036854775807) - __INT64_C(1), __INT64_C(9223372036854775807), - dflt ? 0 : 10, &num, contextnode) - || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { - goto error; - } - - if (make_canonical(ctx, LY_TYPE_INT64, value_, &num, NULL) == -1) { - goto error; - } - - if (store) { - /* store the result */ - val->int64 = num; - *val_type = LY_TYPE_INT64; - } - break; - - case LY_TYPE_UINT8: - if (parse_uint(value, __UINT64_C(255), dflt ? 0 : 10, &unum, contextnode) - || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { - goto error; - } - - if (make_canonical(ctx, LY_TYPE_UINT8, value_, &unum, NULL) == -1) { - goto error; - } - - if (store) { - /* store the result */ - val->uint8 = (uint8_t)unum; - *val_type = LY_TYPE_UINT8; - } - break; - - case LY_TYPE_UINT16: - if (parse_uint(value, __UINT64_C(65535), dflt ? 0 : 10, &unum, contextnode) - || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { - goto error; - } - - if (make_canonical(ctx, LY_TYPE_UINT16, value_, &unum, NULL) == -1) { - goto error; - } - - if (store) { - /* store the result */ - val->uint16 = (uint16_t)unum; - *val_type = LY_TYPE_UINT16; - } - break; - - case LY_TYPE_UINT32: - if (parse_uint(value, __UINT64_C(4294967295), dflt ? 0 : 10, &unum, contextnode) - || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { - goto error; - } - - if (make_canonical(ctx, LY_TYPE_UINT32, value_, &unum, NULL) == -1) { - goto error; - } - - if (store) { - /* store the result */ - val->uint32 = (uint32_t)unum; - *val_type = LY_TYPE_UINT32; - } - break; - - case LY_TYPE_UINT64: - if (parse_uint(value, __UINT64_C(18446744073709551615), dflt ? 0 : 10, &unum, contextnode) - || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { - goto error; - } - - if (make_canonical(ctx, LY_TYPE_UINT64, value_, &unum, NULL) == -1) { - goto error; - } - - if (store) { - /* store the result */ - val->uint64 = unum; - *val_type = LY_TYPE_UINT64; - } - break; - - case LY_TYPE_UNION: - if (store) { - /* unresolved union type */ - memset(val, 0, sizeof(lyd_val)); - *val_type = LY_TYPE_UNION; - } - - if (type->info.uni.has_ptr_type) { - /* we are not resolving anything here, only parsing, and in this case we cannot decide - * the type without resolving it -> we return the union type (resolve it with resolve_union()) */ - if (xml) { - /* in case it should resolve into a instance-identifier, we can only do the JSON conversion here */ - ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); - val->string = transform_xml2json(ctx, value, xml, 1, 1); - ly_ilo_restore(NULL, prev_ilo, NULL, 0); - if (!val->string) { - /* invalid instance-identifier format, likely some other type */ - val->string = lydict_insert(ctx, value, 0); - } - } - break; - } - - t = NULL; - found = 0; - - /* turn logging off, we are going to try to validate the value with all the types in order */ - ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); - - while ((t = lyp_get_next_union_type(type, t, &found))) { - found = 0; - ret = lyp_parse_value(t, value_, xml, leaf, attr, NULL, store, dflt, 0); - if (ret) { - /* we have the result */ - type = ret; - break; - } - - if (store) { - /* erase possible present and invalid value data */ - lyd_free_value(*val, *val_type, *val_flags, t, *value_, NULL, NULL, NULL); - memset(val, 0, sizeof(lyd_val)); - } - } - - /* turn logging back on */ - ly_ilo_restore(NULL, prev_ilo, NULL, 0); - - if (!t) { - /* not found */ - if (store) { - *val_type = 0; - } - if (leaf) { - LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_ ? *value_ : "", itemname); - } else { - LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); - } - goto error; - } - break; - - default: - LOGINT(ctx); - goto error; - } - - /* search user types in case this value is supposed to be stored in a custom way */ - if (store && type->der && type->der->module) { - c = lytype_store(type->der->module, type->der->name, value_, val); - if (c == -1) { - goto error; - } else if (!c) { - *val_flags |= LY_VALUE_USER; - } - } - - /* free backup */ - if (store) { - lyd_free_value(old_val, old_val_type, old_val_flags, type, old_val_str, NULL, NULL, NULL); - lydict_remove(ctx, old_val_str); - } - return type; - -error: - /* restore the backup */ - if (store) { - *val = old_val; - *val_type = old_val_type; - *val_flags = old_val_flags; - lydict_remove(ctx, old_val_str); - } - return NULL; -} - -/* does not log, cannot fail */ -struct lys_type * -lyp_get_next_union_type(struct lys_type *type, struct lys_type *prev_type, int *found) -{ - unsigned int i; - struct lys_type *ret = NULL; - - while (!type->info.uni.count) { - assert(type->der); /* at least the direct union type has to have type specified */ - type = &type->der->type; - } - - for (i = 0; i < type->info.uni.count; ++i) { - if (type->info.uni.types[i].base == LY_TYPE_UNION) { - ret = lyp_get_next_union_type(&type->info.uni.types[i], prev_type, found); - if (ret) { - break; - } - continue; - } - - if (!prev_type || *found) { - ret = &type->info.uni.types[i]; - break; - } - - if (&type->info.uni.types[i] == prev_type) { - *found = 1; - } - } - - return ret; -} - -/* ret 0 - ret set, ret 1 - ret not set, no log, ret -1 - ret not set, fatal error */ -int -lyp_fill_attr(struct ly_ctx *ctx, struct lyd_node *parent, const char *module_ns, const char *module_name, - const char *attr_name, const char *attr_value, struct lyxml_elem *xml, int options, struct lyd_attr **ret) -{ - const struct lys_module *mod = NULL; - const struct lys_submodule *submod = NULL; - struct lys_type **type; - struct lyd_attr *dattr; - int pos, i, j, k; - - /* first, get module where the annotation should be defined */ - if (module_ns) { - mod = (struct lys_module *)ly_ctx_get_module_by_ns(ctx, module_ns, NULL, 0); - } else if (module_name) { - mod = (struct lys_module *)ly_ctx_get_module(ctx, module_name, NULL, 0); - } else { - LOGINT(ctx); - return -1; - } - if (!mod) { - return 1; - } - - /* then, find the appropriate annotation definition */ - pos = -1; - for (i = 0, j = 0; i < mod->ext_size; i = i + j + 1) { - j = lys_ext_instance_presence(&ctx->models.list[0]->extensions[0], &mod->ext[i], mod->ext_size - i); - if (j == -1) { - break; - } - if (ly_strequal(mod->ext[i + j]->arg_value, attr_name, 0)) { - pos = i + j; - break; - } - } - - /* try submodules */ - if (pos == -1) { - for (k = 0; k < mod->inc_size; ++k) { - submod = mod->inc[k].submodule; - for (i = 0, j = 0; i < submod->ext_size; i = i + j + 1) { - j = lys_ext_instance_presence(&ctx->models.list[0]->extensions[0], &submod->ext[i], submod->ext_size - i); - if (j == -1) { - break; - } - if (ly_strequal(submod->ext[i + j]->arg_value, attr_name, 0)) { - pos = i + j; - break; - } - } - } - } - - if (pos == -1) { - return 1; - } - - /* allocate and fill the data attribute structure */ - dattr = calloc(1, sizeof *dattr); - LY_CHECK_ERR_RETURN(!dattr, LOGMEM(ctx), -1); - - dattr->parent = parent; - dattr->next = NULL; - dattr->annotation = submod ? (struct lys_ext_instance_complex *)submod->ext[pos] : - (struct lys_ext_instance_complex *)mod->ext[pos]; - dattr->name = lydict_insert(ctx, attr_name, 0); - dattr->value_str = lydict_insert(ctx, attr_value, 0); - - /* the value is here converted to a JSON format if needed in case of LY_TYPE_IDENT and LY_TYPE_INST or to a - * canonical form of the value */ - type = lys_ext_complex_get_substmt(LY_STMT_TYPE, dattr->annotation, NULL); - if (!type || !lyp_parse_value(*type, &dattr->value_str, xml, NULL, dattr, NULL, 1, 0, options & LYD_OPT_TRUSTED)) { - lydict_remove(ctx, dattr->name); - lydict_remove(ctx, dattr->value_str); - free(dattr); - return -1; - } - - *ret = dattr; - return 0; -} - -int -lyp_check_edit_attr(struct ly_ctx *ctx, struct lyd_attr *attr, struct lyd_node *parent, int *editbits) -{ - struct lyd_attr *last = NULL; - int bits = 0; - - /* 0x01 - insert attribute present - * 0x02 - insert is relative (before or after) - * 0x04 - value attribute present - * 0x08 - key attribute present - * 0x10 - operation attribute present - * 0x20 - operation not allowing insert attribute (delete or remove) - */ - LY_TREE_FOR(attr, attr) { - last = NULL; - if (!strcmp(attr->annotation->arg_value, "operation") && - !strcmp(attr->annotation->module->name, "ietf-netconf")) { - if (bits & 0x10) { - LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "operation attributes", parent->schema->name); - return -1; - } - - bits |= 0x10; - if (attr->value.enm->value >= 3) { - /* delete or remove */ - bits |= 0x20; - } - } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ - !strcmp(attr->annotation->arg_value, "insert")) { - /* 'insert' attribute present */ - if (!(parent->schema->flags & LYS_USERORDERED)) { - /* ... but it is not expected */ - LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, "insert"); - return -1; - } - if (bits & 0x01) { - LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "insert attributes", parent->schema->name); - return -1; - } - - bits |= 0x01; - if (attr->value.enm->value >= 2) { - /* before or after */ - bits |= 0x02; - } - last = attr; - } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ - !strcmp(attr->annotation->arg_value, "value")) { - if (bits & 0x04) { - LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "value attributes", parent->schema->name); - return -1; - } else if (parent->schema->nodetype & LYS_LIST) { - LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, attr->name); - return -1; - } - bits |= 0x04; - last = attr; - } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ - !strcmp(attr->annotation->arg_value, "key")) { - if (bits & 0x08) { - LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "key attributes", parent->schema->name); - return -1; - } else if (parent->schema->nodetype & LYS_LEAFLIST) { - LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, attr->name); - return -1; - } - bits |= 0x08; - last = attr; - } - } - - /* report errors */ - if (last && (!(parent->schema->nodetype & (LYS_LEAFLIST | LYS_LIST)) || !(parent->schema->flags & LYS_USERORDERED))) { - /* moving attributes in wrong elements (not an user ordered list or not a list at all) */ - LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, last->name); - return -1; - } else if (bits == 3) { - /* 0x01 | 0x02 - relative position, but value/key is missing */ - if (parent->schema->nodetype & LYS_LIST) { - LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, parent, "key", parent->schema->name); - } else { /* LYS_LEAFLIST */ - LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, parent, "value", parent->schema->name); - } - return -1; - } else if ((bits & (0x04 | 0x08)) && !(bits & 0x02)) { - /* key/value without relative position */ - LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, (bits & 0x04) ? "value" : "key"); - return -1; - } else if ((bits & 0x21) == 0x21) { - /* insert in delete/remove */ - LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, "insert"); - return -1; - } - - if (editbits) { - *editbits = bits; - } - return 0; -} - -/* does not log */ -static int -dup_identity_check(const char *id, struct lys_ident *ident, uint32_t size) -{ - uint32_t i; - - for (i = 0; i < size; i++) { - if (ly_strequal(id, ident[i].name, 1)) { - /* name collision */ - return EXIT_FAILURE; - } - } - - return EXIT_SUCCESS; -} - -int -dup_identities_check(const char *id, struct lys_module *module) -{ - struct lys_module *mainmod; - int i; - - if (dup_identity_check(id, module->ident, module->ident_size)) { - LOGVAL(module->ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "identity", id); - return EXIT_FAILURE; - } - - /* check identity in submodules */ - mainmod = lys_main_module(module); - for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; ++i) { - if (dup_identity_check(id, mainmod->inc[i].submodule->ident, mainmod->inc[i].submodule->ident_size)) { - LOGVAL(module->ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "identity", id); - return EXIT_FAILURE; - } - } - - return EXIT_SUCCESS; -} - -/* does not log */ -int -dup_typedef_check(const char *type, struct lys_tpdf *tpdf, int size) -{ - int i; - - for (i = 0; i < size; i++) { - if (!strcmp(type, tpdf[i].name)) { - /* name collision */ - return EXIT_FAILURE; - } - } - - return EXIT_SUCCESS; -} - -/* does not log */ -static int -dup_feature_check(const char *id, struct lys_module *module) -{ - int i; - - for (i = 0; i < module->features_size; i++) { - if (!strcmp(id, module->features[i].name)) { - return EXIT_FAILURE; - } - } - - return EXIT_SUCCESS; -} - -/* does not log */ -static int -dup_prefix_check(const char *prefix, struct lys_module *module) -{ - int i; - - if (module->prefix && !strcmp(module->prefix, prefix)) { - return EXIT_FAILURE; - } - for (i = 0; i < module->imp_size; i++) { - if (!strcmp(module->imp[i].prefix, prefix)) { - return EXIT_FAILURE; - } - } - - return EXIT_SUCCESS; -} - -/* logs directly */ -int -lyp_check_identifier(struct ly_ctx *ctx, const char *id, enum LY_IDENT type, struct lys_module *module, - struct lys_node *parent) -{ - int i, j; - int size; - struct lys_tpdf *tpdf; - struct lys_node *node; - struct lys_module *mainmod; - struct lys_submodule *submod; - - assert(ctx && id); - - /* check id syntax */ - if (!(id[0] >= 'A' && id[0] <= 'Z') && !(id[0] >= 'a' && id[0] <= 'z') && id[0] != '_') { - LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "invalid start character"); - return EXIT_FAILURE; - } - for (i = 1; id[i]; i++) { - if (!(id[i] >= 'A' && id[i] <= 'Z') && !(id[i] >= 'a' && id[i] <= 'z') - && !(id[i] >= '0' && id[i] <= '9') && id[i] != '_' && id[i] != '-' && id[i] != '.') { - LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "invalid character"); - return EXIT_FAILURE; - } - } - - if (i > 64) { - LOGWRN(ctx, "Identifier \"%s\" is long, you should use something shorter.", id); - } - - switch (type) { - case LY_IDENT_NAME: - /* check uniqueness of the node within its siblings */ - if (!parent) { - break; - } - - LY_TREE_FOR(parent->child, node) { - if (ly_strequal(node->name, id, 1)) { - LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "name duplication"); - return EXIT_FAILURE; - } - } - break; - case LY_IDENT_TYPE: - assert(module); - mainmod = lys_main_module(module); - - /* check collision with the built-in types */ - if (!strcmp(id, "binary") || !strcmp(id, "bits") || - !strcmp(id, "boolean") || !strcmp(id, "decimal64") || - !strcmp(id, "empty") || !strcmp(id, "enumeration") || - !strcmp(id, "identityref") || !strcmp(id, "instance-identifier") || - !strcmp(id, "int8") || !strcmp(id, "int16") || - !strcmp(id, "int32") || !strcmp(id, "int64") || - !strcmp(id, "leafref") || !strcmp(id, "string") || - !strcmp(id, "uint8") || !strcmp(id, "uint16") || - !strcmp(id, "uint32") || !strcmp(id, "uint64") || !strcmp(id, "union")) { - LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, id, "typedef"); - LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Typedef name duplicates a built-in type."); - return EXIT_FAILURE; - } - - /* check locally scoped typedefs (avoid name shadowing) */ - for (; parent; parent = lys_parent(parent)) { - switch (parent->nodetype) { - case LYS_CONTAINER: - size = ((struct lys_node_container *)parent)->tpdf_size; - tpdf = ((struct lys_node_container *)parent)->tpdf; - break; - case LYS_LIST: - size = ((struct lys_node_list *)parent)->tpdf_size; - tpdf = ((struct lys_node_list *)parent)->tpdf; - break; - case LYS_GROUPING: - size = ((struct lys_node_grp *)parent)->tpdf_size; - tpdf = ((struct lys_node_grp *)parent)->tpdf; - break; - default: - continue; - } - - if (dup_typedef_check(id, tpdf, size)) { - LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); - return EXIT_FAILURE; - } - } - - /* check top-level names */ - if (dup_typedef_check(id, module->tpdf, module->tpdf_size)) { - LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); - return EXIT_FAILURE; - } - - /* check submodule's top-level names */ - for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; i++) { - if (dup_typedef_check(id, mainmod->inc[i].submodule->tpdf, mainmod->inc[i].submodule->tpdf_size)) { - LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); - return EXIT_FAILURE; - } - } - - break; - case LY_IDENT_PREFIX: - assert(module); - - /* check the module itself */ - if (dup_prefix_check(id, module)) { - LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "prefix", id); - return EXIT_FAILURE; - } - break; - case LY_IDENT_FEATURE: - assert(module); - mainmod = lys_main_module(module); - - /* check feature name uniqueness*/ - /* check features in the current module */ - if (dup_feature_check(id, module)) { - LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "feature", id); - return EXIT_FAILURE; - } - - /* and all its submodules */ - for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; i++) { - if (dup_feature_check(id, (struct lys_module *)mainmod->inc[i].submodule)) { - LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "feature", id); - return EXIT_FAILURE; - } - } - break; - - case LY_IDENT_EXTENSION: - assert(module); - mainmod = lys_main_module(module); - - /* check extension name uniqueness in the main module ... */ - for (i = 0; i < mainmod->extensions_size; i++) { - if (ly_strequal(id, mainmod->extensions[i].name, 1)) { - LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "extension", id); - return EXIT_FAILURE; - } - } - - /* ... and all its submodules */ - for (j = 0; j < mainmod->inc_size && mainmod->inc[j].submodule; j++) { - submod = mainmod->inc[j].submodule; /* shortcut */ - for (i = 0; i < submod->extensions_size; i++) { - if (ly_strequal(id, submod->extensions[i].name, 1)) { - LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "extension", id); - return EXIT_FAILURE; - } - } - } - - break; - - default: - /* no check required */ - break; - } - - return EXIT_SUCCESS; -} - -/* logs directly */ -int -lyp_check_date(struct ly_ctx *ctx, const char *date) -{ - int i; - struct tm tm, tm_; - char *r; - - assert(date); - - /* check format */ - for (i = 0; i < LY_REV_SIZE - 1; i++) { - if (i == 4 || i == 7) { - if (date[i] != '-') { - goto error; - } - } else if (!isdigit(date[i])) { - goto error; - } - } - - /* check content, e.g. 2018-02-31 */ - memset(&tm, 0, sizeof tm); - r = strptime(date, "%Y-%m-%d", &tm); - if (!r || r != &date[LY_REV_SIZE - 1]) { - goto error; - } - /* set some arbitrary non-0 value in case DST changes, it could move the day otherwise */ - tm.tm_hour = 12; - - memcpy(&tm_, &tm, sizeof tm); - mktime(&tm_); /* mktime modifies tm_ if it refers invalid date */ - if (tm.tm_mday != tm_.tm_mday) { /* e.g 2018-02-29 -> 2018-03-01 */ - /* checking days is enough, since other errors - * have been checked by strptime() */ - goto error; - } - - return EXIT_SUCCESS; - -error: - LOGVAL(ctx, LYE_INDATE, LY_VLOG_NONE, NULL, date); - return EXIT_FAILURE; -} - -/** - * @return - * NULL - success - * root - not yet resolvable - * other node - mandatory node under the root - */ -static const struct lys_node * -lyp_check_mandatory_(const struct lys_node *root) -{ - int mand_flag = 0; - const struct lys_node *iter = NULL; - - while ((iter = lys_getnext(iter, root, NULL, LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHUSES | LYS_GETNEXT_INTOUSES - | LYS_GETNEXT_INTONPCONT | LYS_GETNEXT_NOSTATECHECK))) { - if (iter->nodetype == LYS_USES) { - if (!((struct lys_node_uses *)iter)->grp) { - /* not yet resolved uses */ - return root; - } else { - /* go into uses */ - continue; - } - } - if (iter->nodetype == LYS_CHOICE) { - /* skip it, it was already checked for direct mandatory node in default */ - continue; - } - if (iter->nodetype == LYS_LIST) { - if (((struct lys_node_list *)iter)->min) { - mand_flag = 1; - } - } else if (iter->nodetype == LYS_LEAFLIST) { - if (((struct lys_node_leaflist *)iter)->min) { - mand_flag = 1; - } - } else if (iter->flags & LYS_MAND_TRUE) { - mand_flag = 1; - } - - if (mand_flag) { - return iter; - } - } - - return NULL; -} - -/* logs directly */ -int -lyp_check_mandatory_augment(struct lys_node_augment *aug, const struct lys_node *target) -{ - const struct lys_node *node; - - if (aug->when || target->nodetype == LYS_CHOICE) { - /* - mandatory nodes in new cases are ok; - * clarification from YANG 1.1 - augmentation can add mandatory nodes when it is - * conditional with a when statement */ - return EXIT_SUCCESS; - } - - if ((node = lyp_check_mandatory_((struct lys_node *)aug))) { - if (node != (struct lys_node *)aug) { - LOGVAL(target->module->ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "mandatory"); - LOGVAL(target->module->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, - "Mandatory node \"%s\" appears in augment of \"%s\" without when condition.", - node->name, aug->target_name); - return -1; - } - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -/** - * @brief check that a mandatory node is not directly under the default case. - * @param[in] node choice with default node - * @return EXIT_SUCCESS if the constraint is fulfilled, EXIT_FAILURE otherwise - */ -int -lyp_check_mandatory_choice(struct lys_node *node) -{ - const struct lys_node *mand, *dflt = ((struct lys_node_choice *)node)->dflt; - - if ((mand = lyp_check_mandatory_(dflt))) { - if (mand != dflt) { - LOGVAL(node->module->ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "mandatory"); - LOGVAL(node->module->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, - "Mandatory node \"%s\" is directly under the default case \"%s\" of the \"%s\" choice.", - mand->name, dflt->name, node->name); - return -1; - } - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -/** - * @brief Check status for invalid combination. - * - * @param[in] flags1 Flags of the referencing node. - * @param[in] mod1 Module of the referencing node, - * @param[in] name1 Schema node name of the referencing node. - * @param[in] flags2 Flags of the referenced node. - * @param[in] mod2 Module of the referenced node, - * @param[in] name2 Schema node name of the referenced node. - * @return EXIT_SUCCES on success, EXIT_FAILURE on invalid reference. - */ -int -lyp_check_status(uint16_t flags1, struct lys_module *mod1, const char *name1, - uint16_t flags2, struct lys_module *mod2, const char *name2, - const struct lys_node *node) -{ - uint16_t flg1, flg2; - - flg1 = (flags1 & LYS_STATUS_MASK) ? (flags1 & LYS_STATUS_MASK) : LYS_STATUS_CURR; - flg2 = (flags2 & LYS_STATUS_MASK) ? (flags2 & LYS_STATUS_MASK) : LYS_STATUS_CURR; - - if ((flg1 < flg2) && (lys_main_module(mod1) == lys_main_module(mod2))) { - LOGVAL(mod1->ctx, LYE_INSTATUS, node ? LY_VLOG_LYS : LY_VLOG_NONE, node, - flg1 == LYS_STATUS_CURR ? "current" : "deprecated", name1, "references", - flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", name2); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -void -lyp_del_includedup(struct lys_module *mod, int free_subs) -{ - struct ly_modules_list *models = &mod->ctx->models; - uint8_t i; - - assert(mod && !mod->type); - - if (models->parsed_submodules_count) { - for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i); - if (models->parsed_submodules[i] == mod) { - if (free_subs) { - for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i) { - lys_sub_module_remove_devs_augs((struct lys_module *)models->parsed_submodules[i]); - lys_submodule_module_data_free((struct lys_submodule *)models->parsed_submodules[i]); - lys_submodule_free((struct lys_submodule *)models->parsed_submodules[i], NULL); - } - } - - models->parsed_submodules_count = i; - if (!models->parsed_submodules_count) { - free(models->parsed_submodules); - models->parsed_submodules = NULL; - } - } - } -} - -static void -lyp_add_includedup(struct lys_module *sub_mod, struct lys_submodule *parsed_submod) -{ - struct ly_modules_list *models = &sub_mod->ctx->models; - int16_t i; - - /* store main module if first include */ - if (models->parsed_submodules_count) { - for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i); - } else { - i = -1; - } - if ((i == -1) || (models->parsed_submodules[i] != lys_main_module(sub_mod))) { - ++models->parsed_submodules_count; - models->parsed_submodules = ly_realloc(models->parsed_submodules, - models->parsed_submodules_count * sizeof *models->parsed_submodules); - LY_CHECK_ERR_RETURN(!models->parsed_submodules, LOGMEM(sub_mod->ctx), ); - models->parsed_submodules[models->parsed_submodules_count - 1] = lys_main_module(sub_mod); - } - - /* store parsed submodule */ - ++models->parsed_submodules_count; - models->parsed_submodules = ly_realloc(models->parsed_submodules, - models->parsed_submodules_count * sizeof *models->parsed_submodules); - LY_CHECK_ERR_RETURN(!models->parsed_submodules, LOGMEM(sub_mod->ctx), ); - models->parsed_submodules[models->parsed_submodules_count - 1] = (struct lys_module *)parsed_submod; -} - -/* - * types: 0 - include, 1 - import - */ -static int -lyp_check_circmod(struct lys_module *module, const char *value, int type) -{ - LY_ECODE code = type ? LYE_CIRC_IMPORTS : LYE_CIRC_INCLUDES; - struct ly_modules_list *models = &module->ctx->models; - uint8_t i; - - /* include/import itself */ - if (ly_strequal(module->name, value, 1)) { - LOGVAL(module->ctx, code, LY_VLOG_NONE, NULL, value); - return -1; - } - - /* currently parsed modules */ - for (i = 0; i < models->parsing_sub_modules_count; i++) { - if (ly_strequal(models->parsing_sub_modules[i]->name, value, 1)) { - LOGVAL(module->ctx, code, LY_VLOG_NONE, NULL, value); - return -1; - } - } - - return 0; -} - -int -lyp_check_circmod_add(struct lys_module *module) -{ - struct ly_modules_list *models = &module->ctx->models; - - /* storing - enlarge the list of modules being currently parsed */ - ++models->parsing_sub_modules_count; - models->parsing_sub_modules = ly_realloc(models->parsing_sub_modules, - models->parsing_sub_modules_count * sizeof *models->parsing_sub_modules); - LY_CHECK_ERR_RETURN(!models->parsing_sub_modules, LOGMEM(module->ctx), -1); - models->parsing_sub_modules[models->parsing_sub_modules_count - 1] = module; - - return 0; -} - -void -lyp_check_circmod_pop(struct ly_ctx *ctx) -{ - if (!ctx->models.parsing_sub_modules_count) { - LOGINT(ctx); - return; - } - - /* update the list of currently being parsed modules */ - ctx->models.parsing_sub_modules_count--; - if (!ctx->models.parsing_sub_modules_count) { - free(ctx->models.parsing_sub_modules); - ctx->models.parsing_sub_modules = NULL; - } -} - -/* - * -1 - error - invalid duplicities) - * 0 - success, no duplicity - * 1 - success, valid duplicity found and stored in *sub - */ -static int -lyp_check_includedup(struct lys_module *mod, const char *name, struct lys_include *inc, struct lys_submodule **sub) -{ - struct lys_module **parsed_sub = mod->ctx->models.parsed_submodules; - uint8_t i, parsed_sub_count = mod->ctx->models.parsed_submodules_count; - - assert(sub); - - for (i = 0; i < mod->inc_size; ++i) { - if (ly_strequal(mod->inc[i].submodule->name, name, 1)) { - /* the same module is already included in the same module - error */ - LOGVAL(mod->ctx, LYE_INARG, LY_VLOG_NONE, NULL, name, "include"); - LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Submodule \"%s\" included twice in the same module \"%s\".", - name, mod->name); - return -1; - } - } - - if (parsed_sub_count) { - assert(!parsed_sub[0]->type); - for (i = parsed_sub_count - 1; parsed_sub[i]->type; --i) { - if (ly_strequal(parsed_sub[i]->name, name, 1)) { - /* check revisions, including multiple revisions of a single module is error */ - if (inc->rev[0] && (!parsed_sub[i]->rev_size || strcmp(parsed_sub[i]->rev[0].date, inc->rev))) { - /* the already included submodule has - * - no revision, but here we require some - * - different revision than the one required here */ - LOGVAL(mod->ctx, LYE_INARG, LY_VLOG_NONE, NULL, name, "include"); - LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Including multiple revisions of submodule \"%s\".", name); - return -1; - } - - /* the same module is already included in some other submodule, return it */ - (*sub) = (struct lys_submodule *)parsed_sub[i]; - return 1; - } - } - } - - /* no duplicity found */ - return 0; -} - -/* returns: - * 0 - inc successfully filled - * -1 - error - */ -int -lyp_check_include(struct lys_module *module, const char *value, struct lys_include *inc, struct unres_schema *unres) -{ - int i; - - /* check that the submodule was not included yet */ - i = lyp_check_includedup(module, value, inc, &inc->submodule); - if (i == -1) { - return -1; - } else if (i == 1) { - return 0; - } - /* submodule is not yet loaded */ - - /* circular include check */ - if (lyp_check_circmod(module, value, 0)) { - return -1; - } - - /* try to load the submodule */ - inc->submodule = (struct lys_submodule *)ly_ctx_load_sub_module(module->ctx, module, value, - inc->rev[0] ? inc->rev : NULL, 1, unres); - - /* check the result */ - if (!inc->submodule) { - if (ly_errno != LY_EVALID) { - LOGVAL(module->ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "include"); - } - LOGERR(module->ctx, LY_EVALID, "Including \"%s\" module into \"%s\" failed.", value, module->name); - return -1; - } - - /* check the revision */ - if (inc->rev[0] && inc->submodule->rev_size && strcmp(inc->rev, inc->submodule->rev[0].date)) { - LOGERR(module->ctx, LY_EVALID, "\"%s\" include of submodule \"%s\" in revision \"%s\" not found.", - module->name, value, inc->rev); - unres_schema_free((struct lys_module *)inc->submodule, &unres, 0); - lys_sub_module_remove_devs_augs((struct lys_module *)inc->submodule); - lys_submodule_module_data_free((struct lys_submodule *)inc->submodule); - lys_submodule_free(inc->submodule, NULL); - inc->submodule = NULL; - return -1; - } - - /* store the submodule as successfully parsed */ - lyp_add_includedup(module, inc->submodule); - - return 0; -} - -static int -lyp_check_include_missing_recursive(struct lys_module *main_module, struct lys_submodule *sub) -{ - uint8_t i, j; - void *reallocated; - int ret = 0, tmp; - struct ly_ctx *ctx = main_module->ctx; - - for (i = 0; i < sub->inc_size; i++) { - /* check that the include is also present in the main module */ - for (j = 0; j < main_module->inc_size; j++) { - if (main_module->inc[j].submodule == sub->inc[i].submodule) { - break; - } - } - - if (j == main_module->inc_size) { - /* match not found */ - if (main_module->version >= LYS_VERSION_1_1) { - LOGVAL(ctx, LYE_MISSSTMT, LY_VLOG_NONE, NULL, "include"); - LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, - "The main module \"%s\" misses include of the \"%s\" submodule used in another submodule \"%s\".", - main_module->name, sub->inc[i].submodule->name, sub->name); - /* now we should return error, but due to the issues with freeing the module, we actually have - * to go through the all includes and, as in case of 1.0, add them into the main module and fail - * at the end when all the includes are in the main module and we can free them */ - ret = 1; - } else { - /* not strictly an error in YANG 1.0 */ - LOGWRN(ctx, "The main module \"%s\" misses include of the \"%s\" submodule used in another submodule \"%s\".", - main_module->name, sub->inc[i].submodule->name, sub->name); - LOGWRN(ctx, "To avoid further issues, adding submodule \"%s\" into the main module \"%s\".", - sub->inc[i].submodule->name, main_module->name); - /* but since it is a good practise and because we expect all the includes in the main module - * when searching it and also when freeing the module, put it into it */ - } - main_module->inc_size++; - reallocated = realloc(main_module->inc, main_module->inc_size * sizeof *main_module->inc); - LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx), 1); - main_module->inc = reallocated; - memset(&main_module->inc[main_module->inc_size - 1], 0, sizeof *main_module->inc); - /* to avoid unexpected consequences, copy just a link to the submodule and the revision, - * all other substatements of the include are ignored */ - memcpy(&main_module->inc[main_module->inc_size - 1].rev, sub->inc[i].rev, LY_REV_SIZE - 1); - main_module->inc[main_module->inc_size - 1].submodule = sub->inc[i].submodule; - } - - /* recursion */ - tmp = lyp_check_include_missing_recursive(main_module, sub->inc[i].submodule); - if (!ret && tmp) { - ret = 1; - } - } - - return ret; -} - -int -lyp_check_include_missing(struct lys_module *main_module) -{ - int ret = 0; - uint8_t i; - - /* in YANG 1.1, all the submodules must be in the main module, check it even for - * 1.0 where it will be printed as warning and the include will be added into the main module */ - for (i = 0; i < main_module->inc_size; i++) { - if (lyp_check_include_missing_recursive(main_module, main_module->inc[i].submodule)) { - ret = 1; - } - } - - return ret; -} - -/* returns: - * 0 - imp successfully filled - * -1 - error, imp not cleaned - */ -int -lyp_check_import(struct lys_module *module, const char *value, struct lys_import *imp) -{ - int i; - struct lys_module *dup = NULL; - struct ly_ctx *ctx = module->ctx; - - /* check for importing a single module in multiple revisions */ - for (i = 0; i < module->imp_size; i++) { - if (!module->imp[i].module) { - /* skip the not yet filled records */ - continue; - } - if (ly_strequal(module->imp[i].module->name, value, 1)) { - /* check revisions, including multiple revisions of a single module is error */ - if (imp->rev[0] && (!module->imp[i].module->rev_size || strcmp(module->imp[i].module->rev[0].date, imp->rev))) { - /* the already imported module has - * - no revision, but here we require some - * - different revision than the one required here */ - LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "import"); - LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Importing multiple revisions of module \"%s\".", value); - return -1; - } else if (!imp->rev[0]) { - /* no revision, remember the duplication, but check revisions after loading the module - * because the current revision can be the same (then it is ok) or it can differ (then it - * is error */ - dup = module->imp[i].module; - break; - } - - /* there is duplication, but since prefixes differs (checked in caller of this function), - * it is ok */ - imp->module = module->imp[i].module; - return 0; - } - } - - /* circular import check */ - if (lyp_check_circmod(module, value, 1)) { - return -1; - } - - /* load module - in specific situations it tries to get the module from the context */ - imp->module = (struct lys_module *)ly_ctx_load_sub_module(module->ctx, NULL, value, imp->rev[0] ? imp->rev : NULL, - module->ctx->models.flags & LY_CTX_ALLIMPLEMENTED ? 1 : 0, - NULL); - - /* check the result */ - if (!imp->module) { - LOGERR(ctx, LY_EVALID, "Importing \"%s\" module into \"%s\" failed.", value, module->name); - return -1; - } - - if (imp->rev[0] && imp->module->rev_size && strcmp(imp->rev, imp->module->rev[0].date)) { - LOGERR(ctx, LY_EVALID, "\"%s\" import of module \"%s\" in revision \"%s\" not found.", - module->name, value, imp->rev); - return -1; - } - - if (dup) { - /* check the revisions */ - if ((dup != imp->module) || - (dup->rev_size != imp->module->rev_size && (!dup->rev_size || imp->module->rev_size)) || - (dup->rev_size && strcmp(dup->rev[0].date, imp->module->rev[0].date))) { - /* - modules are not the same - * - one of modules has no revision (except they both has no revision) - * - revisions of the modules are not the same */ - LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "import"); - LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Importing multiple revisions of module \"%s\".", value); - return -1; - } else { - LOGWRN(ctx, "Module \"%s\" is imported by \"%s\" multiple times with different prefixes.", dup->name, module->name); - } - } - - return 0; -} - -/* - * put the newest revision to the first position - */ -void -lyp_sort_revisions(struct lys_module *module) -{ - uint8_t i, r; - struct lys_revision rev; - - for (i = 1, r = 0; i < module->rev_size; i++) { - if (strcmp(module->rev[i].date, module->rev[r].date) > 0) { - r = i; - } - } - - if (r) { - /* the newest revision is not on position 0, switch them */ - memcpy(&rev, &module->rev[0], sizeof rev); - memcpy(&module->rev[0], &module->rev[r], sizeof rev); - memcpy(&module->rev[r], &rev, sizeof rev); - } -} - -void -lyp_ext_instance_rm(struct ly_ctx *ctx, struct lys_ext_instance ***ext, uint8_t *size, uint8_t index) -{ - uint8_t i; - - lys_extension_instances_free(ctx, (*ext)[index]->ext, (*ext)[index]->ext_size, NULL); - lydict_remove(ctx, (*ext)[index]->arg_value); - free((*ext)[index]); - - /* move the rest of the array */ - for (i = index + 1; i < (*size); i++) { - (*ext)[i - 1] = (*ext)[i]; - } - /* clean the last cell in the array structure */ - (*ext)[(*size) - 1] = NULL; - /* the array is not reallocated here, just change its size */ - (*size) = (*size) - 1; - - if (!(*size)) { - /* ext array is empty */ - free((*ext)); - ext = NULL; - } -} - -static int -lyp_rfn_apply_ext_(struct lys_refine *rfn, struct lys_node *target, LYEXT_SUBSTMT substmt, struct lys_ext *extdef) -{ - struct ly_ctx *ctx; - int m, n; - struct lys_ext_instance *new; - void *reallocated; - - ctx = target->module->ctx; /* shortcut */ - - m = n = -1; - while ((m = lys_ext_iter(rfn->ext, rfn->ext_size, m + 1, substmt)) != -1) { - /* refine's substatement includes extensions, copy them to the target, replacing the previous - * substatement's extensions if any. In case of refining the extension itself, we are going to - * replace only the same extension (pointing to the same definition) */ - if (substmt == LYEXT_SUBSTMT_SELF && rfn->ext[m]->def != extdef) { - continue; - } - - /* get the index of the extension to replace in the target node */ - do { - n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); - } while (n != -1 && substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef); - - /* TODO cover complex extension instances */ - if (n == -1) { - /* nothing to replace, we are going to add it - reallocate */ - new = malloc(sizeof **target->ext); - LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); - reallocated = realloc(target->ext, (target->ext_size + 1) * sizeof *target->ext); - LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx); free(new), EXIT_FAILURE); - target->ext = reallocated; - target->ext_size++; - - /* init */ - n = target->ext_size - 1; - target->ext[n] = new; - target->ext[n]->parent = target; - target->ext[n]->parent_type = LYEXT_PAR_NODE; - target->ext[n]->flags = 0; - target->ext[n]->insubstmt = substmt; - target->ext[n]->priv = NULL; - target->ext[n]->nodetype = LYS_EXT; - target->ext[n]->module = target->module; - } else { - /* replacing - first remove the allocated data from target */ - lys_extension_instances_free(ctx, target->ext[n]->ext, target->ext[n]->ext_size, NULL); - lydict_remove(ctx, target->ext[n]->arg_value); - } - /* common part for adding and replacing */ - target->ext[n]->def = rfn->ext[m]->def; - /* parent and parent_type do not change */ - target->ext[n]->arg_value = lydict_insert(ctx, rfn->ext[m]->arg_value, 0); - /* flags do not change */ - target->ext[n]->ext_size = rfn->ext[m]->ext_size; - lys_ext_dup(ctx, target->module, rfn->ext[m]->ext, rfn->ext[m]->ext_size, target, LYEXT_PAR_NODE, - &target->ext[n]->ext, 0, NULL); - /* substmt does not change, but the index must be taken from the refine */ - target->ext[n]->insubstmt_index = rfn->ext[m]->insubstmt_index; - } - - /* remove the rest of extensions belonging to the original substatement in the target node */ - while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt)) != -1) { - if (substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef) { - /* keep this extension */ - continue; - } - - /* remove the item */ - lyp_ext_instance_rm(ctx, &target->ext, &target->ext_size, n); - --n; - } - - return EXIT_SUCCESS; -} - -/* - * apply extension instances defined under refine's substatements. - * It cannot be done immediately when applying the refine because there can be - * still unresolved data (e.g. type) and mainly the targeted extension instances. - */ -int -lyp_rfn_apply_ext(struct lys_module *module) -{ - int i, k, a = 0; - struct lys_node *root, *nextroot, *next, *node; - struct lys_node *target; - struct lys_node_uses *uses; - struct lys_refine *rfn; - struct ly_set *extset; - - /* refines in uses */ - LY_TREE_FOR_SAFE(module->data, nextroot, root) { - /* go through the data tree of the module and all the defined augments */ - - LY_TREE_DFS_BEGIN(root, next, node) { - if (node->nodetype == LYS_USES) { - uses = (struct lys_node_uses *)node; - - for (i = 0; i < uses->refine_size; i++) { - if (!uses->refine[i].ext_size) { - /* no extensions in refine */ - continue; - } - rfn = &uses->refine[i]; /* shortcut */ - - /* get the target node */ - target = NULL; - resolve_descendant_schema_nodeid(rfn->target_name, uses->child, - LYS_NO_RPC_NOTIF_NODE | LYS_ACTION | LYS_NOTIF, - 0, (const struct lys_node **)&target); - if (!target) { - /* it should always succeed since the target_name was already resolved at least - * once when the refine itself was being resolved */ - LOGINT(module->ctx);; - return EXIT_FAILURE; - } - - /* extensions */ - extset = ly_set_new(); - k = -1; - while ((k = lys_ext_iter(rfn->ext, rfn->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { - ly_set_add(extset, rfn->ext[k]->def, 0); - } - for (k = 0; (unsigned int)k < extset->number; k++) { - if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_SELF, (struct lys_ext *)extset->set.g[k])) { - ly_set_free(extset); - return EXIT_FAILURE; - } - } - ly_set_free(extset); - - /* description */ - if (rfn->dsc && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_DESCRIPTION, NULL)) { - return EXIT_FAILURE; - } - /* reference */ - if (rfn->ref && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_REFERENCE, NULL)) { - return EXIT_FAILURE; - } - /* config, in case of notification or rpc/action{notif, the config is not applicable - * (there is no config status) */ - if ((rfn->flags & LYS_CONFIG_MASK) && (target->flags & LYS_CONFIG_MASK)) { - if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_CONFIG, NULL)) { - return EXIT_FAILURE; - } - } - /* default value */ - if (rfn->dflt_size && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_DEFAULT, NULL)) { - return EXIT_FAILURE; - } - /* mandatory */ - if (rfn->flags & LYS_MAND_MASK) { - if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MANDATORY, NULL)) { - return EXIT_FAILURE; - } - } - /* presence */ - if ((target->nodetype & LYS_CONTAINER) && rfn->mod.presence) { - if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_PRESENCE, NULL)) { - return EXIT_FAILURE; - } - } - /* min/max */ - if (rfn->flags & LYS_RFN_MINSET) { - if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MIN, NULL)) { - return EXIT_FAILURE; - } - } - if (rfn->flags & LYS_RFN_MAXSET) { - if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MAX, NULL)) { - return EXIT_FAILURE; - } - } - /* must and if-feature contain extensions on their own, not needed to be solved here */ - - if (target->ext_size) { - /* the allocated target's extension array can be now longer than needed in case - * there is less refine substatement's extensions than in original. Since we are - * going to reduce or keep the same memory, it is not necessary to test realloc's result */ - target->ext = realloc(target->ext, target->ext_size * sizeof *target->ext); - } - } - } - LY_TREE_DFS_END(root, next, node) - } - - if (!nextroot && a < module->augment_size) { - nextroot = module->augment[a].child; - a++; - } - } - - return EXIT_SUCCESS; -} - -/* - * check mandatory substatements defined under extension instances. - */ -int -lyp_mand_check_ext(struct lys_ext_instance_complex *ext, const char *ext_name) -{ - void *p; - int i; - struct ly_ctx *ctx = ext->module->ctx; - - /* check for mandatory substatements */ - for (i = 0; ext->substmt[i].stmt; i++) { - if (ext->substmt[i].cardinality == LY_STMT_CARD_OPT || ext->substmt[i].cardinality == LY_STMT_CARD_ANY) { - /* not a mandatory */ - continue; - } else if (ext->substmt[i].cardinality == LY_STMT_CARD_SOME) { - goto array; - } - - /* - * LY_STMT_ORDEREDBY - not checked, has a default value which is the same as explicit system order - * LY_STMT_MODIFIER, LY_STMT_STATUS, LY_STMT_MANDATORY, LY_STMT_CONFIG - checked, but mandatory requirement - * does not make sense since there is also a default value specified - */ - switch (ext->substmt[i].stmt) { - case LY_STMT_ORDEREDBY: - /* always ok */ - break; - case LY_STMT_REQINSTANCE: - case LY_STMT_DIGITS: - case LY_STMT_MODIFIER: - p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); - if (!*(uint8_t*)p) { - LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); - goto error; - } - break; - case LY_STMT_STATUS: - p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); - if (!(*(uint16_t*)p & LYS_STATUS_MASK)) { - LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); - goto error; - } - break; - case LY_STMT_MANDATORY: - p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); - if (!(*(uint16_t*)p & LYS_MAND_MASK)) { - LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); - goto error; - } - break; - case LY_STMT_CONFIG: - p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); - if (!(*(uint16_t*)p & LYS_CONFIG_MASK)) { - LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); - goto error; - } - break; - default: -array: - /* stored as a pointer */ - p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); - if (!(*(void**)p)) { - LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); - goto error; - } - break; - } - } - - return EXIT_SUCCESS; - -error: - return EXIT_FAILURE; -} - -static int -lyp_deviate_del_ext(struct lys_node *target, struct lys_ext_instance *ext) -{ - int n = -1, found = 0; - char *path; - - while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, ext->insubstmt)) != -1) { - if (target->ext[n]->def != ext->def) { - continue; - } - - if (ext->def->argument) { - /* check matching arguments */ - if (!ly_strequal(target->ext[n]->arg_value, ext->arg_value, 1)) { - continue; - } - } - - /* we have the matching extension - remove it */ - ++found; - lyp_ext_instance_rm(target->module->ctx, &target->ext, &target->ext_size, n); - --n; - } - - if (!found) { - path = lys_path(target, LYS_PATH_FIRST_PREFIX); - LOGERR(target->module->ctx, LY_EVALID, "Extension deviation: extension \"%s\" to delete not found in \"%s\".", - ext->def->name, path) - free(path); - return EXIT_FAILURE; - } - return EXIT_SUCCESS; -} - -static int -lyp_deviate_apply_ext(struct lys_deviate *dev, struct lys_node *target, LYEXT_SUBSTMT substmt, struct lys_ext *extdef) -{ - struct ly_ctx *ctx; - int m, n; - struct lys_ext_instance *new; - void *reallocated; - - /* LY_DEVIATE_ADD and LY_DEVIATE_RPL are very similar so they are implement the same way - in replacing, - * there can be some extension instances in the target, in case of adding, there should not be any so we - * will be just adding. */ - - ctx = target->module->ctx; /* shortcut */ - m = n = -1; - - while ((m = lys_ext_iter(dev->ext, dev->ext_size, m + 1, substmt)) != -1) { - /* deviate and its substatements include extensions, copy them to the target, replacing the previous - * extensions if any. In case of deviating extension itself, we have to deviate only the same type - * of the extension as specified in the deviation */ - if (substmt == LYEXT_SUBSTMT_SELF && dev->ext[m]->def != extdef) { - continue; - } - - if (substmt == LYEXT_SUBSTMT_SELF && dev->mod == LY_DEVIATE_ADD) { - /* in case of adding extension, we will be replacing only the inherited extensions */ - do { - n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); - } while (n != -1 && (target->ext[n]->def != extdef || !(target->ext[n]->flags & LYEXT_OPT_INHERIT))); - } else { - /* get the index of the extension to replace in the target node */ - do { - n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); - /* if we are applying extension deviation, we have to deviate only the same type of the extension */ - } while (n != -1 && substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef); - } - - if (n == -1) { - /* nothing to replace, we are going to add it - reallocate */ - new = malloc(sizeof **target->ext); - LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); - reallocated = realloc(target->ext, (target->ext_size + 1) * sizeof *target->ext); - LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx); free(new), EXIT_FAILURE); - target->ext = reallocated; - target->ext_size++; - - n = target->ext_size - 1; - } else { - /* replacing - the original set of extensions is actually backuped together with the - * node itself, so we are supposed only to free the allocated data here ... */ - lys_extension_instances_free(ctx, target->ext[n]->ext, target->ext[n]->ext_size, NULL); - lydict_remove(ctx, target->ext[n]->arg_value); - free(target->ext[n]); - - /* and prepare the new structure */ - new = malloc(sizeof **target->ext); - LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); - } - /* common part for adding and replacing - fill the newly created / replaced cell */ - target->ext[n] = new; - target->ext[n]->def = dev->ext[m]->def; - target->ext[n]->arg_value = lydict_insert(ctx, dev->ext[m]->arg_value, 0); - target->ext[n]->flags = 0; - target->ext[n]->parent = target; - target->ext[n]->parent_type = LYEXT_PAR_NODE; - target->ext[n]->insubstmt = substmt; - target->ext[n]->insubstmt_index = dev->ext[m]->insubstmt_index; - target->ext[n]->ext_size = dev->ext[m]->ext_size; - lys_ext_dup(ctx, target->module, dev->ext[m]->ext, dev->ext[m]->ext_size, target, LYEXT_PAR_NODE, - &target->ext[n]->ext, 1, NULL); - target->ext[n]->nodetype = LYS_EXT; - target->ext[n]->module = target->module; - target->ext[n]->priv = NULL; - - /* TODO cover complex extension instances */ - } - - /* remove the rest of extensions belonging to the original substatement in the target node, - * due to possible reverting of the deviation effect, they are actually not removed, just moved - * to the backup of the original node when the original node is backuped, here we just have to - * free the replaced / deleted originals */ - while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt)) != -1) { - if (substmt == LYEXT_SUBSTMT_SELF) { - /* if we are applying extension deviation, we are going to remove only - * - the same type of the extension in case of replacing - * - the same type of the extension which was inherited in case of adding - * note - delete deviation is covered in lyp_deviate_del_ext */ - if (target->ext[n]->def != extdef || - (dev->mod == LY_DEVIATE_ADD && !(target->ext[n]->flags & LYEXT_OPT_INHERIT))) { - /* keep this extension */ - continue; - } - - } - - /* remove the item */ - lyp_ext_instance_rm(ctx, &target->ext, &target->ext_size, n); - --n; - } - - return EXIT_SUCCESS; -} - -/* - * not-supported deviations are not processed since they affect the complete node, not just their substatements - */ -int -lyp_deviation_apply_ext(struct lys_module *module) -{ - int i, j, k; - struct lys_deviate *dev; - struct lys_node *target; - struct ly_set *extset; - - for (i = 0; i < module->deviation_size; i++) { - target = NULL; - extset = NULL; - j = resolve_schema_nodeid(module->deviation[i].target_name, NULL, module, &extset, 0, 0); - if (j == -1) { - return EXIT_FAILURE; - } else if (!extset) { - /* LY_DEVIATE_NO */ - ly_set_free(extset); - continue; - } - target = extset->set.s[0]; - ly_set_free(extset); - - for (j = 0; j < module->deviation[i].deviate_size; j++) { - dev = &module->deviation[i].deviate[j]; - if (!dev->ext_size) { - /* no extensions in deviate and its substatement, nothing to do here */ - continue; - } - - /* extensions */ - if (dev->mod == LY_DEVIATE_DEL) { - k = -1; - while ((k = lys_ext_iter(dev->ext, dev->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { - if (lyp_deviate_del_ext(target, dev->ext[k])) { - return EXIT_FAILURE; - } - } - - /* In case of LY_DEVIATE_DEL, we are applying only extension deviation, removing - * of the substatement's extensions was already done when the substatement was applied. - * Extension deviation could not be applied by the parser since the extension could be unresolved, - * which is not the issue of the other substatements. */ - continue; - } else { - extset = ly_set_new(); - k = -1; - while ((k = lys_ext_iter(dev->ext, dev->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { - ly_set_add(extset, dev->ext[k]->def, 0); - } - for (k = 0; (unsigned int)k < extset->number; k++) { - if (lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_SELF, (struct lys_ext *)extset->set.g[k])) { - ly_set_free(extset); - return EXIT_FAILURE; - } - } - ly_set_free(extset); - } - - /* unique */ - if (dev->unique_size && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_UNIQUE, NULL)) { - return EXIT_FAILURE; - } - /* units */ - if (dev->units && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_UNITS, NULL)) { - return EXIT_FAILURE; - } - /* default */ - if (dev->dflt_size && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_DEFAULT, NULL)) { - return EXIT_FAILURE; - } - /* config */ - if ((dev->flags & LYS_CONFIG_MASK) && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_CONFIG, NULL)) { - return EXIT_FAILURE; - } - /* mandatory */ - if ((dev->flags & LYS_MAND_MASK) && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MANDATORY, NULL)) { - return EXIT_FAILURE; - } - /* min/max */ - if (dev->min_set && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MIN, NULL)) { - return EXIT_FAILURE; - } - if (dev->min_set && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MAX, NULL)) { - return EXIT_FAILURE; - } - /* type and must contain extension instances in their structures */ - } - } - - return EXIT_SUCCESS; -} - -int -lyp_ctx_check_module(struct lys_module *module) -{ - struct ly_ctx *ctx; - int i, match_i = -1, to_implement; - const char *last_rev = NULL; - - assert(module); - to_implement = 0; - ctx = module->ctx; - - /* find latest revision */ - for (i = 0; i < module->rev_size; ++i) { - if (!last_rev || (strcmp(last_rev, module->rev[i].date) < 0)) { - last_rev = module->rev[i].date; - } - } - - for (i = 0; i < ctx->models.used; i++) { - /* check name (name/revision) and namespace uniqueness */ - if (!strcmp(ctx->models.list[i]->name, module->name)) { - if (to_implement) { - if (i == match_i) { - continue; - } - LOGERR(ctx, LY_EINVAL, "Module \"%s@%s\" in another revision \"%s\" already implemented.", - module->name, last_rev ? last_rev : "", ctx->models.list[i]->rev[0].date); - return -1; - } else if (!ctx->models.list[i]->rev_size && module->rev_size) { - LOGERR(ctx, LY_EINVAL, "Module \"%s\" without revision already in context.", module->name); - return -1; - } else if (ctx->models.list[i]->rev_size && !module->rev_size) { - LOGERR(ctx, LY_EINVAL, "Module \"%s\" with revision \"%s\" already in context.", - module->name, ctx->models.list[i]->rev[0].date); - return -1; - } else if ((!module->rev_size && !ctx->models.list[i]->rev_size) - || !strcmp(ctx->models.list[i]->rev[0].date, last_rev)) { - - LOGVRB("Module \"%s@%s\" already in context.", module->name, last_rev ? last_rev : ""); - - /* if disabled, enable first */ - if (ctx->models.list[i]->disabled) { - lys_set_enabled(ctx->models.list[i]); - } - - to_implement = module->implemented; - match_i = i; - if (to_implement && !ctx->models.list[i]->implemented) { - /* check first that it is okay to change it to implemented */ - i = -1; - continue; - } - return 1; - - } else if (module->implemented && ctx->models.list[i]->implemented) { - LOGERR(ctx, LY_EINVAL, "Module \"%s@%s\" in another revision \"%s\" already implemented.", - module->name, last_rev ? last_rev : "", ctx->models.list[i]->rev[0].date); - return -1; - } - /* else keep searching, for now the caller is just adding - * another revision of an already present schema - */ - } else if (!strcmp(ctx->models.list[i]->ns, module->ns)) { - LOGERR(ctx, LY_EINVAL, "Two different modules (\"%s\" and \"%s\") have the same namespace \"%s\".", - ctx->models.list[i]->name, module->name, module->ns); - return -1; - } - } - - if (to_implement) { - if (lys_set_implemented(ctx->models.list[match_i])) { - return -1; - } - return 1; - } - - return 0; -} - -int -lyp_ctx_add_module(struct lys_module *module) -{ - struct lys_module **newlist = NULL; - int i; - - assert(!lyp_ctx_check_module(module)); - -#ifndef NDEBUG - int j; - /* check that all augments are resolved */ - for (i = 0; i < module->augment_size; ++i) { - assert(module->augment[i].target); - } - for (i = 0; i < module->inc_size; ++i) { - for (j = 0; j < module->inc[i].submodule->augment_size; ++j) { - assert(module->inc[i].submodule->augment[j].target); - } - } -#endif - - /* add to the context's list of modules */ - if (module->ctx->models.used == module->ctx->models.size) { - newlist = realloc(module->ctx->models.list, (2 * module->ctx->models.size) * sizeof *newlist); - LY_CHECK_ERR_RETURN(!newlist, LOGMEM(module->ctx), -1); - for (i = module->ctx->models.size; i < module->ctx->models.size * 2; i++) { - newlist[i] = NULL; - } - module->ctx->models.size *= 2; - module->ctx->models.list = newlist; - } - module->ctx->models.list[module->ctx->models.used++] = module; - module->ctx->models.module_set_id++; - - return 0; -} - -/** - * Store UTF-8 character specified as 4byte integer into the dst buffer. - * Returns number of written bytes (4 max), expects that dst has enough space. - * - * UTF-8 mapping: - * 00000000 -- 0000007F: 0xxxxxxx - * 00000080 -- 000007FF: 110xxxxx 10xxxxxx - * 00000800 -- 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx - * 00010000 -- 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * - * Includes checking for valid characters (following RFC 7950, sec 9.4) - */ -unsigned int -pututf8(struct ly_ctx *ctx, char *dst, int32_t value) -{ - if (value < 0x80) { - /* one byte character */ - if (value < 0x20 && - value != 0x09 && - value != 0x0a && - value != 0x0d) { - goto error; - } - - dst[0] = value; - return 1; - } else if (value < 0x800) { - /* two bytes character */ - dst[0] = 0xc0 | (value >> 6); - dst[1] = 0x80 | (value & 0x3f); - return 2; - } else if (value < 0xfffe) { - /* three bytes character */ - if (((value & 0xf800) == 0xd800) || - (value >= 0xfdd0 && value <= 0xfdef)) { - /* exclude surrogate blocks %xD800-DFFF */ - /* exclude noncharacters %xFDD0-FDEF */ - goto error; - } - - dst[0] = 0xe0 | (value >> 12); - dst[1] = 0x80 | ((value >> 6) & 0x3f); - dst[2] = 0x80 | (value & 0x3f); - - return 3; - } else if (value < 0x10fffe) { - if ((value & 0xffe) == 0xffe) { - /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF, - * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF, - * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */ - goto error; - } - /* four bytes character */ - dst[0] = 0xf0 | (value >> 18); - dst[1] = 0x80 | ((value >> 12) & 0x3f); - dst[2] = 0x80 | ((value >> 6) & 0x3f); - dst[3] = 0x80 | (value & 0x3f); - - return 4; - } - -error: - /* out of range */ - LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, NULL); - LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); - return 0; -} - -unsigned int -copyutf8(struct ly_ctx *ctx, char *dst, const char *src) -{ - uint32_t value; - - /* unicode characters */ - if (!(src[0] & 0x80)) { - /* one byte character */ - if (src[0] < 0x20 && - src[0] != 0x09 && - src[0] != 0x0a && - src[0] != 0x0d) { - LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); - LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%02x", src[0]); - return 0; - } - - dst[0] = src[0]; - return 1; - } else if (!(src[0] & 0x20)) { - /* two bytes character */ - dst[0] = src[0]; - dst[1] = src[1]; - return 2; - } else if (!(src[0] & 0x10)) { - /* three bytes character */ - value = ((uint32_t)(src[0] & 0xf) << 12) | ((uint32_t)(src[1] & 0x3f) << 6) | (src[2] & 0x3f); - if (((value & 0xf800) == 0xd800) || - (value >= 0xfdd0 && value <= 0xfdef) || - (value & 0xffe) == 0xffe) { - /* exclude surrogate blocks %xD800-DFFF */ - /* exclude noncharacters %xFDD0-FDEF */ - /* exclude noncharacters %xFFFE-FFFF */ - LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); - LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); - return 0; - } - - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - return 3; - } else if (!(src[0] & 0x08)) { - /* four bytes character */ - value = ((uint32_t)(src[0] & 0x7) << 18) | ((uint32_t)(src[1] & 0x3f) << 12) | ((uint32_t)(src[2] & 0x3f) << 6) | (src[3] & 0x3f); - if ((value & 0xffe) == 0xffe) { - /* exclude noncharacters %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF, - * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF, - * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */ - LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); - LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); - return 0; - } - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = src[3]; - return 4; - } else { - LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); - LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 leading byte 0x%02x", src[0]); - return 0; - } -} - -const struct lys_module * -lyp_get_module(const struct lys_module *module, const char *prefix, int pref_len, const char *name, int name_len, int in_data) -{ - const struct lys_module *main_module; - char *str; - int i; - - assert(!prefix || !name); - - if (prefix && !pref_len) { - pref_len = strlen(prefix); - } - if (name && !name_len) { - name_len = strlen(name); - } - - main_module = lys_main_module(module); - - /* module own prefix, submodule own prefix, (sub)module own name */ - if ((!prefix || (!module->type && !strncmp(main_module->prefix, prefix, pref_len) && !main_module->prefix[pref_len]) - || (module->type && !strncmp(module->prefix, prefix, pref_len) && !module->prefix[pref_len])) - && (!name || (!strncmp(main_module->name, name, name_len) && !main_module->name[name_len]))) { - return main_module; - } - - /* standard import */ - for (i = 0; i < module->imp_size; ++i) { - if ((!prefix || (!strncmp(module->imp[i].prefix, prefix, pref_len) && !module->imp[i].prefix[pref_len])) - && (!name || (!strncmp(module->imp[i].module->name, name, name_len) && !module->imp[i].module->name[name_len]))) { - return module->imp[i].module; - } - } - - /* module required by a foreign grouping, deviation, or submodule */ - if (name) { - str = strndup(name, name_len); - if (!str) { - LOGMEM(module->ctx); - return NULL; - } - main_module = ly_ctx_get_module(module->ctx, str, NULL, 0); - - /* try data callback */ - if (!main_module && in_data && module->ctx->data_clb) { - main_module = module->ctx->data_clb(module->ctx, str, NULL, 0, module->ctx->data_clb_data); - } - - free(str); - return main_module; - } - - return NULL; -} - -const struct lys_module * -lyp_get_import_module_ns(const struct lys_module *module, const char *ns) -{ - int i; - const struct lys_module *mod = NULL; - - assert(module && ns); - - if (module->type) { - /* the module is actually submodule and to get the namespace, we need the main module */ - if (ly_strequal(((struct lys_submodule *)module)->belongsto->ns, ns, 0)) { - return ((struct lys_submodule *)module)->belongsto; - } - } else { - /* module's own namespace */ - if (ly_strequal(module->ns, ns, 0)) { - return module; - } - } - - /* imported modules */ - for (i = 0; i < module->imp_size; ++i) { - if (ly_strequal(module->imp[i].module->ns, ns, 0)) { - return module->imp[i].module; - } - } - - return mod; -} - -const char * -lyp_get_yang_data_template_name(const struct lyd_node *node) -{ - struct lys_node *snode; - - snode = lys_parent(node->schema); - while (snode && snode->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE)) { - snode = lys_parent(snode); - } - - if (snode && snode->nodetype == LYS_EXT && strcmp(((struct lys_ext_instance_complex *)snode)->def->name, "yang-data") == 0) { - return ((struct lys_ext_instance_complex *)snode)->arg_value; - } else { - return NULL; - } -} - -const struct lys_node * -lyp_get_yang_data_template(const struct lys_module *module, const char *yang_data_name, int yang_data_name_len) -{ - int i, j; - const struct lys_node *ret = NULL; - const struct lys_submodule *submodule; - - for (i = 0; i < module->ext_size; ++i) { - if (!strcmp(module->ext[i]->def->name, "yang-data") && !strncmp(module->ext[i]->arg_value, yang_data_name, yang_data_name_len) - && !module->ext[i]->arg_value[yang_data_name_len]) { - ret = (struct lys_node *)module->ext[i]; - break; - } - } - - for (j = 0; !ret && j < module->inc_size; ++j) { - submodule = module->inc[j].submodule; - for (i = 0; i < submodule->ext_size; ++i) { - if (!strcmp(submodule->ext[i]->def->name, "yang-data") && !strncmp(submodule->ext[i]->arg_value, yang_data_name, yang_data_name_len) - && !submodule->ext[i]->arg_value[yang_data_name_len]) { - ret = (struct lys_node *)submodule->ext[i]; - break; - } - } - } - - return ret; -} diff --git a/test/bug-hunting/cve/CVE-2019-19888/expected.txt b/test/bug-hunting/cve/CVE-2019-19888/expected.txt deleted file mode 100644 index 5935b965267..00000000000 --- a/test/bug-hunting/cve/CVE-2019-19888/expected.txt +++ /dev/null @@ -1 +0,0 @@ -jfif.c:430:bughuntingDivByZero diff --git a/test/bug-hunting/cve/CVE-2019-19888/jfif.c b/test/bug-hunting/cve/CVE-2019-19888/jfif.c deleted file mode 100644 index 48c9211ed6c..00000000000 --- a/test/bug-hunting/cve/CVE-2019-19888/jfif.c +++ /dev/null @@ -1,862 +0,0 @@ -/* °üº¬Í·Îļþ */ -#include -#include -#include -#include "stdefine.h" -#include "bitstr.h" -#include "huffman.h" -#include "quant.h" -#include "zigzag.h" -#include "dct.h" -#include "bmp.h" -#include "color.h" -#include "jfif.h" - -// Ô¤±àÒ뿪¹Ø -#define DEBUG_JFIF 0 - -// ÄÚ²¿ÀàÐͶ¨Òå -typedef struct { - // width & height - int width; - int height; - - // quantization table - int *pqtab[16]; - - // huffman codec ac - HUFCODEC *phcac[16]; - - // huffman codec dc - HUFCODEC *phcdc[16]; - - // components - int comp_num; - struct { - int id; - int samp_factor_v; - int samp_factor_h; - int qtab_idx; - int htab_idx_ac; - int htab_idx_dc; - } comp_info[4]; - - int datalen; - BYTE *databuf; -} JFIF; - -/* ÄÚ²¿º¯ÊýʵÏÖ */ -#if DEBUG_JFIF -static void jfif_dump(JFIF *jfif) -{ - int i, j; - - printf("++ jfif dump ++\n"); - printf("width : %d\n", jfif->width ); - printf("height: %d\n", jfif->height); - printf("\n"); - - for (i=0; i<16; i++) { - if (!jfif->pqtab[i]) continue; - printf("qtab%d\n", i); - for (j=0; j<64; j++) { - printf("%3d,%c", jfif->pqtab[i][j], j%8 == 7 ? '\n' : ' '); - } - printf("\n"); - } - - for (i=0; i<16; i++) { - int size = 16; - if (!jfif->phcac[i]) continue; - printf("htabac%d\n", i); - for (j=0; j<16; j++) { - size += jfif->phcac[i]->huftab[j]; - } - for (j=0; jphcac[i]->huftab[j], j%16 == 15 ? '\n' : ' '); - } - printf("\n\n"); - } - - for (i=0; i<16; i++) { - int size = 16; - if (!jfif->phcdc[i]) continue; - printf("htabdc%d\n", i); - for (j=0; j<16; j++) { - size += jfif->phcdc[i]->huftab[j]; - } - for (j=0; jphcdc[i]->huftab[j], j%16 == 15 ? '\n' : ' '); - } - printf("\n\n"); - } - - printf("comp_num : %d\n", jfif->comp_num); - for (i=0; icomp_num; i++) { - printf("id:%d samp_factor_v:%d samp_factor_h:%d qtab_idx:%d htab_idx_ac:%d htab_idx_dc:%d\n", - jfif->comp_info[i].id, - jfif->comp_info[i].samp_factor_v, - jfif->comp_info[i].samp_factor_h, - jfif->comp_info[i].qtab_idx, - jfif->comp_info[i].htab_idx_ac, - jfif->comp_info[i].htab_idx_dc); - } - printf("\n"); - - printf("datalen : %d\n", jfif->datalen); - printf("-- jfif dump --\n"); -} - -static void dump_du(int *du) -{ - int i; - for (i=0; i<64; i++) { - printf("%3d%c", du[i], i % 8 == 7 ? '\n' : ' '); - } - printf("\n"); -} -#endif - -static int ALIGN(int x, int y) { - // y must be a power of 2. - return (x + y - 1) & ~(y - 1); -} - -static void category_encode(int *code, int *size) -{ - unsigned absc = abs(*code); - unsigned mask = (1 << 15); - int i = 15; - if (absc == 0) { *size = 0; return; } - while (i && !(absc & mask)) { mask >>= 1; i--; } - *size = i + 1; - if (*code < 0) *code = (1 << *size) - absc - 1; -} - -static int category_decode(int code, int size) -{ - return code >= (1 << (size - 1)) ? code : code - (1 << size) + 1; -} - -/* º¯ÊýʵÏÖ */ -void* jfif_load(char *file) -{ - JFIF *jfif = NULL; - FILE *fp = NULL; - int header = 0; - int type = 0; - WORD size = 0; - BYTE *buf = NULL; - BYTE *end = NULL; - BYTE *dqt, *dht; - int ret =-1; - long offset = 0; - int i; - - jfif = calloc(1, sizeof(JFIF)); - buf = calloc(1, 0x10000); - end = buf + 0x10000; - if (!jfif || !buf) goto done; - - fp = fopen(file, "rb"); - if (!fp) goto done; - - while (1) { - do { header = fgetc(fp); } while (header != EOF && header != 0xff); // get header - do { type = fgetc(fp); } while (type != EOF && type == 0xff); // get type - if (header == EOF || type == EOF) { - printf("file eof !\n"); - break; - } - - if ((type == 0xd8) || (type == 0xd9) || (type == 0x01) || (type >= 0xd0 && type <= 0xd7)) { - size = 0; - } else { - size = fgetc(fp) << 8; - size |= fgetc(fp) << 0; - size -= 2; - } - - size = fread(buf, 1, size, fp); - switch (type) { - case 0xc0: // SOF0 - jfif->width = (buf[3] << 8) | (buf[4] << 0); - jfif->height = (buf[1] << 8) | (buf[2] << 0); - jfif->comp_num = buf[5] < 4 ? buf[5] : 4; - for (i=0; icomp_num; i++) { - jfif->comp_info[i].id = buf[6 + i * 3]; - jfif->comp_info[i].samp_factor_v = (buf[7 + i * 3] >> 0) & 0x0f; - jfif->comp_info[i].samp_factor_h = (buf[7 + i * 3] >> 4) & 0x0f; - jfif->comp_info[i].qtab_idx = buf[8 + i * 3] & 0x0f; - } - break; - - case 0xda: // SOS - jfif->comp_num = buf[0] < 4 ? buf[0] : 4; - for (i=0; icomp_num; i++) { - jfif->comp_info[i].id = buf[1 + i * 2]; - jfif->comp_info[i].htab_idx_ac = (buf[2 + i * 2] >> 0) & 0x0f; - jfif->comp_info[i].htab_idx_dc = (buf[2 + i * 2] >> 4) & 0x0f; - } - offset = ftell(fp); - ret = 0; - goto read_data; - - case 0xdb: // DQT - dqt = buf; - while (size > 0 && dqt < end) { - int idx = dqt[0] & 0x0f; - int f16 = dqt[0] & 0xf0; - if (!jfif->pqtab[idx]) jfif->pqtab[idx] = malloc(64 * sizeof(int)); - if (!jfif->pqtab[idx]) break; - if (dqt + 1 + 64 + (f16 ? 64 : 0) < end) { - for (i=0; i<64; i++) { - jfif->pqtab[idx][ZIGZAG[i]] = f16 ? ((dqt[1 + i * 2] << 8) | (dqt[2 + i * 2] << 0)) : dqt[1 + i]; - } - } - dqt += 1 + 64 + (f16 ? 64 : 0); - size-= 1 + 64 + (f16 ? 64 : 0); - } - break; - - case 0xc4: // DHT - dht = buf; - while (size > 0 && dht + 17 < end) { - int idx = dht[0] & 0x0f; - int fac = dht[0] & 0xf0; - int len = 0; - for (i=1; i<1+16; i++) len += dht[i]; - if (len > end - dht - 17) len = end - dht - 17; - if (len > 256) len = 256; - if (fac) { - if (!jfif->phcac[idx]) jfif->phcac[idx] = calloc(1, sizeof(HUFCODEC)); - if (jfif->phcac[idx]) memcpy(jfif->phcac[idx]->huftab, &dht[1], 16 + len); - } else { - if (!jfif->phcdc[idx]) jfif->phcdc[idx] = calloc(1, sizeof(HUFCODEC)); - if (jfif->phcdc[idx]) memcpy(jfif->phcdc[idx]->huftab, &dht[1], 16 + len); - } - dht += 17 + len; - size-= 17 + len; - } - break; - } - } - -read_data: - fseek(fp, 0, SEEK_END); - jfif->datalen = ftell(fp) - offset; - jfif->databuf = malloc(jfif->datalen); - if (jfif->databuf) { - fseek(fp, offset, SEEK_SET); - fread(jfif->databuf, 1, jfif->datalen, fp); - } - -done: - if (buf) free (buf); - if (fp) fclose(fp ); - if (ret == -1) { - jfif_free(jfif); - jfif = NULL; - } - return jfif; -} - -int jfif_save(void *ctxt, char *file) -{ - JFIF *jfif = (JFIF*)ctxt; - FILE *fp = NULL; - int len = 0; - int i, j; - int ret = -1; - - fp = fopen(file, "wb"); - if (!fp) goto done; - - // output SOI - fputc(0xff, fp); - fputc(0xd8, fp); - - // output DQT - for (i=0; i<16; i++) { - if (!jfif->pqtab[i]) continue; - len = 2 + 1 + 64; - fputc(0xff, fp); - fputc(0xdb, fp); - fputc(len >> 8, fp); - fputc(len >> 0, fp); - fputc(i, fp); - for (j=0; j<64; j++) { - fputc(jfif->pqtab[i][ZIGZAG[j]], fp); - } - } - - // output SOF0 - len = 2 + 1 + 2 + 2 + 1 + 3 * jfif->comp_num; - fputc(0xff, fp); - fputc(0xc0, fp); - fputc(len >> 8, fp); - fputc(len >> 0, fp); - fputc(8, fp); // precision 8bit - fputc(jfif->height >> 8, fp); // height - fputc(jfif->height >> 0, fp); // height - fputc(jfif->width >> 8, fp); // width - fputc(jfif->width >> 0, fp); // width - fputc(jfif->comp_num, fp); - for (i=0; icomp_num; i++) { - fputc(jfif->comp_info[i].id, fp); - fputc((jfif->comp_info[i].samp_factor_v << 0)|(jfif->comp_info[i].samp_factor_h << 4), fp); - fputc(jfif->comp_info[i].qtab_idx, fp); - } - - // output DHT AC - for (i=0; i<16; i++) { - if (!jfif->phcac[i]) continue; - fputc(0xff, fp); - fputc(0xc4, fp); - len = 2 + 1 + 16; - for (j=0; j<16; j++) len += jfif->phcac[i]->huftab[j]; - fputc(len >> 8, fp); - fputc(len >> 0, fp); - fputc(i + 0x10, fp); - fwrite(jfif->phcac[i]->huftab, len - 3, 1, fp); - } - - // output DHT DC - for (i=0; i<16; i++) { - if (!jfif->phcdc[i]) continue; - fputc(0xff, fp); - fputc(0xc4, fp); - len = 2 + 1 + 16; - for (j=0; j<16; j++) len += jfif->phcdc[i]->huftab[j]; - fputc(len >> 8, fp); - fputc(len >> 0, fp); - fputc(i + 0x00, fp); - fwrite(jfif->phcdc[i]->huftab, len - 3, 1, fp); - } - - // output SOS - len = 2 + 1 + 2 * jfif->comp_num + 3; - fputc(0xff, fp); - fputc(0xda, fp); - fputc(len >> 8, fp); - fputc(len >> 0, fp); - fputc(jfif->comp_num, fp); - for (i=0; icomp_num; i++) { - fputc(jfif->comp_info[i].id, fp); - fputc((jfif->comp_info[i].htab_idx_ac << 0)|(jfif->comp_info[i].htab_idx_dc << 4), fp); - } - fputc(0x00, fp); - fputc(0x00, fp); - fputc(0x00, fp); - - // output data - if (jfif->databuf) { - fwrite(jfif->databuf, jfif->datalen, 1, fp); - } - ret = 0; - -done: - if (fp) fclose(fp); - return ret; -} - -void jfif_free(void *ctxt) -{ - JFIF *jfif = (JFIF*)ctxt; - int i; - if (!jfif) return; - for (i=0; i<16; i++) { - if (jfif->pqtab[i]) free(jfif->pqtab[i]); - if (jfif->phcac[i]) free(jfif->phcac[i]); - if (jfif->phcdc[i]) free(jfif->phcdc[i]); - } - if (jfif->databuf) free(jfif->databuf); - free(jfif); -} - -int jfif_decode(void *ctxt, BMP *pb) -{ - JFIF *jfif = (JFIF*)ctxt; - void *bs = NULL; - int *ftab[16]= {0}; - int dc[4] = {0}; - int mcuw, mcuh, mcuc, mcur, mcui, jw, jh; - int i, j, c, h, v, x, y; - int sfh_max = 0; - int sfv_max = 0; - int yuv_stride[3] = {0}; - int yuv_height[3] = {0}; - int *yuv_datbuf[3] = {0}; - int *idst, *isrc; - int *ysrc, *usrc, *vsrc; - BYTE *bdst; - int ret = -1; - - if (!ctxt || !pb) { - printf("invalid input params !\n"); - return -1; - } - - // init dct module - init_dct_module(); - - //++ init ftab - for (i=0; i<16; i++) { - if (jfif->pqtab[i]) { - ftab[i] = malloc(64 * sizeof(int)); - if (ftab[i]) { - init_idct_ftab(ftab[i], jfif->pqtab[i]); - } else { - goto done; - } - } - } - //-- init ftab - - //++ calculate mcu info - for (c=0; ccomp_num; c++) { - if (sfh_max < jfif->comp_info[c].samp_factor_h) { - sfh_max = jfif->comp_info[c].samp_factor_h; - } - if (sfv_max < jfif->comp_info[c].samp_factor_v) { - sfv_max = jfif->comp_info[c].samp_factor_v; - } - } - mcuw = sfh_max * 8; - mcuh = sfv_max * 8; - jw = ALIGN(jfif->width, mcuw); - jh = ALIGN(jfif->height, mcuh); - mcuc = jw / mcuw; - mcur = jh / mcuh; - //-- calculate mcu info - - // create yuv buffer for decoding - yuv_stride[0] = jw; - yuv_stride[1] = jw * jfif->comp_info[1].samp_factor_h / sfh_max; - yuv_stride[2] = jw * jfif->comp_info[2].samp_factor_h / sfh_max; - yuv_height[0] = jh; - yuv_height[1] = jh * jfif->comp_info[1].samp_factor_v / sfv_max; - yuv_height[2] = jh * jfif->comp_info[2].samp_factor_v / sfv_max; - yuv_datbuf[0] = malloc(yuv_stride[0] * yuv_height[0] * sizeof(int)); - yuv_datbuf[1] = malloc(yuv_stride[1] * yuv_height[1] * sizeof(int)); - yuv_datbuf[2] = malloc(yuv_stride[2] * yuv_height[2] * sizeof(int)); - if (!yuv_datbuf[0] || !yuv_datbuf[1] || !yuv_datbuf[2]) { - goto done; - } - - // open bit stream - bs = bitstr_open(jfif->databuf, "mem", jfif->datalen); - if (!bs) { - printf("failed to open bitstr for jfif_decode !"); - return -1; - } - - // init huffman codec - for (i=0; i<16; i++) { - if (jfif->phcac[i]) { - jfif->phcac[i]->input = bs; - huffman_decode_init(jfif->phcac[i]); - } - if (jfif->phcdc[i]) { - jfif->phcdc[i]->input = bs; - huffman_decode_init(jfif->phcdc[i]); - } - } - - for (mcui=0; mcuicomp_num; c++) { - for (v=0; vcomp_info[c].samp_factor_v; v++) { - for (h=0; hcomp_info[c].samp_factor_h; h++) { - HUFCODEC *hcac = jfif->phcac[jfif->comp_info[c].htab_idx_ac]; - HUFCODEC *hcdc = jfif->phcdc[jfif->comp_info[c].htab_idx_dc]; - int fidx = jfif->comp_info[c].qtab_idx; - int size, znum, code; - int du[64] = {0}; - - //+ decode dc - size = huffman_decode_step(hcdc) & 0xf; - if (size) { - code = bitstr_get_bits(bs, size); - code = category_decode(code, size); - } - else { - code = 0; - } - dc[c] += code; - du[0] = dc[c]; - //- decode dc - - //+ decode ac - for (i=1; i<64;) { - code = huffman_decode_step(hcac); - if (code <= 0) break; - size = (code >> 0) & 0xf; - znum = (code >> 4) & 0xf; - i += znum; - code = bitstr_get_bits(bs, size); - code = category_decode(code, size); - if (i < 64) du[i++] = code; - } - //- decode ac - - // de-zigzag - zigzag_decode(du); - - // idct - idct2d8x8(du, ftab[fidx]); - - // copy du to yuv buffer - x = ((mcui % mcuc) * mcuw + h * 8) * jfif->comp_info[c].samp_factor_h / sfh_max; - y = ((mcui / mcuc) * mcuh + v * 8) * jfif->comp_info[c].samp_factor_v / sfv_max; - idst = yuv_datbuf[c] + y * yuv_stride[c] + x; - isrc = du; - for (i=0; i<8; i++) { - memcpy(idst, isrc, 8 * sizeof(int)); - idst += yuv_stride[c]; - isrc += 8; - } - } - } - } - } - - // close huffman codec - for (i=0; i<16; i++) { - if (jfif->phcac[i]) huffman_decode_done(jfif->phcac[i]); - if (jfif->phcdc[i]) huffman_decode_done(jfif->phcdc[i]); - } - - // close bit stream - bitstr_close(bs); - - // create bitmap, and convert yuv to rgb - bmp_create(pb, jfif->width, jfif->height); - bdst = (BYTE*)pb->pdata; - ysrc = yuv_datbuf[0]; - for (i=0; iheight; i++) { - int uy = i * jfif->comp_info[1].samp_factor_v / sfv_max; - int vy = i * jfif->comp_info[2].samp_factor_v / sfv_max; - for (j=0; jwidth; j++) { - int ux = j * jfif->comp_info[1].samp_factor_h / sfh_max; - int vx = j * jfif->comp_info[2].samp_factor_h / sfh_max; - usrc = yuv_datbuf[2] + uy * yuv_stride[2] + ux; - vsrc = yuv_datbuf[1] + vy * yuv_stride[1] + vx; - yuv_to_rgb(*ysrc, *vsrc, *usrc, bdst + 2, bdst + 1, bdst + 0); - bdst += 3; - ysrc += 1; - } - bdst -= jfif->width * 3; - bdst += pb->stride; - ysrc -= jfif->width * 1; - ysrc += yuv_stride[0]; - } - - // success - ret = 0; - -done: - if (yuv_datbuf[0]) free(yuv_datbuf[0]); - if (yuv_datbuf[1]) free(yuv_datbuf[1]); - if (yuv_datbuf[2]) free(yuv_datbuf[2]); - //++ free ftab - for (i=0; i<16; i++) { - if (ftab[i]) { - free(ftab[i]); - } - } - //-- free ftab - return ret; -} - -#define DU_TYPE_LUMIN 0 -#define DU_TYPE_CHROM 1 - -typedef struct { - unsigned runlen : 4; - unsigned codesize : 4; - unsigned codedata : 16; -} RLEITEM; - -static void jfif_encode_du(JFIF *jfif, int type, int du[64], int *dc) -{ - HUFCODEC *hfcac = jfif->phcac[type]; - HUFCODEC *hfcdc = jfif->phcdc[type]; - int *pqtab = jfif->pqtab[type]; - void *bs = hfcac->output; - int diff, code, size; - RLEITEM rlelist[63]; - int i, j, n, eob; - - // fdct - fdct2d8x8(du, NULL); - - // quant - quant_encode(du, pqtab); - - // zigzag - zigzag_encode(du); - - // dc - diff = du[0] - *dc; - *dc = du[0]; - - // category encode for dc - code = diff; - category_encode(&code, &size); - - // huffman encode for dc - huffman_encode_step(hfcdc, size); - bitstr_put_bits(bs, code, size); - - // rle encode for ac - for (i=1, j=0, n=0, eob=0; i<64 && j<63; i++) { - if (du[i] == 0 && n < 15) { - n++; - } else { - code = du[i]; size = 0; - category_encode(&code, &size); - rlelist[j].runlen = n; - rlelist[j].codesize = size; - rlelist[j].codedata = code; - n = 0; - j++; - if (size != 0) eob = j; - } - } - - // set eob - if (du[63] == 0) { - rlelist[eob].runlen = 0; - rlelist[eob].codesize = 0; - rlelist[eob].codedata = 0; - j = eob + 1; - } - - // huffman encode for ac - for (i=0; iwidth = pb->width; - jfif->height = pb->height; - jfif->pqtab[0] = malloc(64*sizeof(int)); - jfif->pqtab[1] = malloc(64*sizeof(int)); - jfif->phcac[0] = calloc(1, sizeof(HUFCODEC)); - jfif->phcac[1] = calloc(1, sizeof(HUFCODEC)); - jfif->phcdc[0] = calloc(1, sizeof(HUFCODEC)); - jfif->phcdc[1] = calloc(1, sizeof(HUFCODEC)); - jfif->datalen = jfif->width * jfif->height * 2; - jfif->databuf = malloc(jfif->datalen); - if (!jfif->pqtab[0] || !jfif->pqtab[1] - || !jfif->phcac[0] || !jfif->phcac[1] - || !jfif->phcdc[0] || !jfif->phcdc[1] - || !jfif->databuf) { - goto done; - } - - // init qtab - memcpy(jfif->pqtab[0], STD_QUANT_TAB_LUMIN, 64*sizeof(int)); - memcpy(jfif->pqtab[1], STD_QUANT_TAB_CHROM, 64*sizeof(int)); - - // open bit stream - bs = bitstr_open(jfif->databuf, "mem", jfif->datalen); - if (!bs) { - printf("failed to open bitstr for jfif_decode !"); - goto done; - } - - // init huffman codec - memcpy(jfif->phcac[0]->huftab, STD_HUFTAB_LUMIN_AC, MAX_HUFFMAN_CODE_LEN + 256); - memcpy(jfif->phcac[1]->huftab, STD_HUFTAB_CHROM_AC, MAX_HUFFMAN_CODE_LEN + 256); - memcpy(jfif->phcdc[0]->huftab, STD_HUFTAB_LUMIN_DC, MAX_HUFFMAN_CODE_LEN + 256); - memcpy(jfif->phcdc[1]->huftab, STD_HUFTAB_CHROM_DC, MAX_HUFFMAN_CODE_LEN + 256); - jfif->phcac[0]->output = bs; huffman_encode_init(jfif->phcac[0], 1); - jfif->phcac[1]->output = bs; huffman_encode_init(jfif->phcac[1], 1); - jfif->phcdc[0]->output = bs; huffman_encode_init(jfif->phcdc[0], 1); - jfif->phcdc[1]->output = bs; huffman_encode_init(jfif->phcdc[1], 1); - - // init comp_num & comp_info - jfif->comp_num = 3; - jfif->comp_info[0].id = 1; - jfif->comp_info[0].samp_factor_v = 2; - jfif->comp_info[0].samp_factor_h = 2; - jfif->comp_info[0].qtab_idx = 0; - jfif->comp_info[0].htab_idx_ac = 0; - jfif->comp_info[0].htab_idx_dc = 0; - jfif->comp_info[1].id = 2; - jfif->comp_info[1].samp_factor_v = 1; - jfif->comp_info[1].samp_factor_h = 1; - jfif->comp_info[1].qtab_idx = 1; - jfif->comp_info[1].htab_idx_ac = 1; - jfif->comp_info[1].htab_idx_dc = 1; - jfif->comp_info[2].id = 3; - jfif->comp_info[2].samp_factor_v = 1; - jfif->comp_info[2].samp_factor_h = 1; - jfif->comp_info[2].qtab_idx = 1; - jfif->comp_info[2].htab_idx_ac = 1; - jfif->comp_info[2].htab_idx_dc = 1; - - // init jw & jw, init yuv data buffer - jw = ALIGN(pb->width, 16); - jh = ALIGN(pb->height, 16); - yuv_datbuf[0] = calloc(1, jw * jh / 1 * sizeof(int)); - yuv_datbuf[1] = calloc(1, jw * jh / 4 * sizeof(int)); - yuv_datbuf[2] = calloc(1, jw * jh / 4 * sizeof(int)); - if (!yuv_datbuf[0] || !yuv_datbuf[1] || !yuv_datbuf[2]) { - goto done; - } - - // convert rgb to yuv - bsrc = pb->pdata; - ydst = yuv_datbuf[0]; - udst = yuv_datbuf[1]; - vdst = yuv_datbuf[2]; - for (i=0; iheight; i++) { - for (j=0; jwidth; j++) { - rgb_to_yuv(bsrc[2], bsrc[1], bsrc[0], ydst, udst, vdst); - bsrc += 3; - ydst += 1; - if (j & 1) { - udst += 1; - vdst += 1; - } - } - bsrc -= pb->width * 3; bsrc += pb->stride; - ydst -= pb->width * 1; ydst += jw; - udst -= pb->width / 2; - vdst -= pb->width / 2; - if (i & 1) { - udst += jw / 2; - vdst += jw / 2; - } - } - - for (m=0; mphcac[0]); - huffman_encode_done(jfif->phcac[1]); - huffman_encode_done(jfif->phcdc[0]); - huffman_encode_done(jfif->phcdc[1]); - jfif->datalen = bitstr_tell(bs); - - // close bit stream - bitstr_close(bs); - - // if failed free context - if (failed) { - jfif_free(jfif); - jfif = NULL; - } - - // return context - return jfif; -} - - - - - - - diff --git a/test/bug-hunting/cve/CVE-2019-7156/expected.txt b/test/bug-hunting/cve/CVE-2019-7156/expected.txt deleted file mode 100644 index ee1ba9430e1..00000000000 --- a/test/bug-hunting/cve/CVE-2019-7156/expected.txt +++ /dev/null @@ -1,4 +0,0 @@ -ole.c:398:bughuntingDivByZero -ole.c:399:bughuntingDivByZero -ole.c:400:bughuntingDivByZero - diff --git a/test/bug-hunting/cve/CVE-2019-7156/ole.c b/test/bug-hunting/cve/CVE-2019-7156/ole.c deleted file mode 100644 index 04467d1b57c..00000000000 --- a/test/bug-hunting/cve/CVE-2019-7156/ole.c +++ /dev/null @@ -1,605 +0,0 @@ -/** - * @file ole.c - * @author Alex Ott, Victor B Wagner - * @date Wed Jun 11 12:33:01 2003 - * Version: $Id: ole.c,v 1.2 2006/02/25 15:28:14 vitus Exp $ - * Copyright: Victor B Wagner, 1996-2003 Alex Ott, 2003 - * - * @brief Parsing structure of MS Office compound document - * - * This file is part of catdoc project - * and distributed under GNU Public License - * - */ -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "catdoc.h" - -#define min(a,b) ((a) < (b) ? (a) : (b)) - -const static unsigned char ole_sign[]={0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1,0}; - - -/** - * Initializes ole structure - * - * @param f (FILE *) compound document file, positioned at bufSize - * byte. Might be pipe or socket - * @param buffer (void *) bytes already read from f - * @param bufSize number of bytes already read from f should be less - * than 512 - * - * @return - */ -FILE* ole_init(FILE *f, void *buffer, size_t bufSize, struct ole_params_t *ole_params) { - unsigned char oleBuf[BBD_BLOCK_SIZE]; - unsigned char *tmpBuf; - FILE *newfile; - int ret=0, i; - long int sbdMaxLen, sbdCurrent, propMaxLen, propCurrent, mblock, msat_size; - oleEntry *tEntry; - long int sectorSize; - long int shortSectorSize; - long int bbdNumBlocks; - - /* deleting old data (if it was allocated) */ - ole_finish(ole_params); - - if (fseek(f,0,SEEK_SET) == -1) { - if (errno == ESPIPE) { - /* We got non-seekable file, create temp file */ - if ((newfile=tmpfile()) == NULL) { - return NULL; - } - if (bufSize > 0) { - ret=fwrite(buffer, 1, bufSize, newfile); - if (ret != bufSize) { - return NULL; - } - } - - while (!feof(f)) { - ret=fread(oleBuf,1,BBD_BLOCK_SIZE,f); - fwrite(oleBuf, 1, ret, newfile); - } - fseek(newfile,0,SEEK_SET); - } else { - return NULL; - } - } else { - newfile=f; - } - fseek(newfile,0,SEEK_END); - ole_params->fileLength=ftell(newfile); - - fseek(newfile,0,SEEK_SET); - ret=fread(oleBuf,1,BBD_BLOCK_SIZE,newfile); - if (ret != BBD_BLOCK_SIZE) { - return NULL; - } - if (strncmp(oleBuf,ole_sign,8) != 0) { - return NULL; - } - ole_params->sectorSize = 1<sectorSize == 0) { - return NULL; - } - sectorSize = ole_params->sectorSize; - ole_params->shortSectorSize = 1<shortSectorSize; - if (shortSectorSize > sectorSize) { - return NULL; - } -/* Read BBD into memory */ - ole_params->bbdNumBlocks = getulong(oleBuf,0x2c); - bbdNumBlocks = ole_params->bbdNumBlocks; - if ((ole_params->BBD=malloc(bbdNumBlocks*sectorSize)) == NULL) { - return NULL; - } - - if ((tmpBuf=malloc(MSAT_ORIG_SIZE)) == NULL) { - return NULL; - } - memcpy(tmpBuf,oleBuf+0x4c,MSAT_ORIG_SIZE); - mblock=getlong(oleBuf,0x44); - msat_size=getlong(oleBuf,0x48); - -/* fprintf(stderr, "msat_size=%ld\n", msat_size); */ - - i=0; - while ((mblock >= 0) && (i < msat_size)) { - unsigned char *newbuf; -/* fprintf(stderr, "i=%d mblock=%ld\n", i, mblock); */ - if ((newbuf=realloc(tmpBuf, sectorSize*(i+1)+MSAT_ORIG_SIZE)) != NULL) { - tmpBuf=newbuf; - } else { - free(tmpBuf); - ole_finish(ole_params); - return NULL; - } - - fseek(newfile, 512+mblock*sectorSize, SEEK_SET); - if (fread(tmpBuf+MSAT_ORIG_SIZE+(sectorSize-4)*i, - 1, sectorSize, newfile) != sectorSize) { - ole_finish(ole_params); - return NULL; - } - - i++; - mblock=getlong(tmpBuf, MSAT_ORIG_SIZE+(sectorSize-4)*i); - } - -/* fprintf(stderr, "bbdNumBlocks=%ld\n", bbdNumBlocks); */ - for (i=0; i< bbdNumBlocks; i++) { - long int bbdSector=getlong(tmpBuf,4*i); - - if (bbdSector >= ole_params->fileLength/sectorSize || bbdSector < 0) { - errno = EINVAL; - ole_finish(ole_params); - return NULL; - } - fseek(newfile, 512+bbdSector*sectorSize, SEEK_SET); - if (fread(ole_params->BBD+i*sectorSize, 1, sectorSize, newfile) != sectorSize) { - free(tmpBuf); - ole_finish(ole_params); - return NULL; - } - } - free(tmpBuf); - -/* Read SBD into memory */ - ole_params->sbdLen=0; - sbdMaxLen=10; - sbdCurrent = ole_params->sbdStart = getlong(oleBuf,0x3c); - if (ole_params->sbdStart > 0) { - if ((ole_params->SBD=malloc(sectorSize*sbdMaxLen)) == NULL) { - ole_finish(ole_params); - return NULL; - } - while (1) { - fseek(newfile, 512+sbdCurrent*sectorSize, SEEK_SET); - fread(ole_params->SBD+ole_params->sbdLen*sectorSize, 1, sectorSize, newfile); - ole_params->sbdLen++; - if (ole_params->sbdLen >= sbdMaxLen) { - unsigned char *newSBD; - - sbdMaxLen+=5; - if ((newSBD=realloc(ole_params->SBD, sectorSize*sbdMaxLen)) != NULL) { - ole_params->SBD=newSBD; - } else { - ole_finish(ole_params); - return NULL; - } - } - if (sbdCurrent < 0 || sbdCurrent * 4 >= bbdNumBlocks * sectorSize) - { - break; - } - sbdCurrent = getlong(ole_params->BBD, sbdCurrent*4); - if (sbdCurrent < 0 || - sbdCurrent >= ole_params->fileLength/sectorSize) - break; - } - ole_params->sbdNumber = (ole_params->sbdLen*sectorSize)/shortSectorSize; - } else { - ole_params->SBD=NULL; - } -/* Read property catalog into memory */ - ole_params->propLen = 0; - propMaxLen = 5; - propCurrent = ole_params->propStart = getlong(oleBuf,0x30); - if (ole_params->propStart >= 0) { - if ((ole_params->properties=malloc(propMaxLen*sectorSize)) == NULL) { - ole_finish(ole_params); - return NULL; - } - while (1) { -/* fprintf(stderr, "propCurrent=%ld\n",propCurrent); */ - fseek(newfile, 512+propCurrent*sectorSize, SEEK_SET); - fread(ole_params->properties+ole_params->propLen*sectorSize, - 1, sectorSize, newfile); - (ole_params->propLen)++; - if (ole_params->propLen >= propMaxLen) { - unsigned char *newProp; - - propMaxLen+=5; - if ((newProp=realloc(ole_params->properties, propMaxLen*sectorSize)) != NULL) - ole_params->properties=newProp; - else { - ole_finish(ole_params); - return NULL; - } - } - - propCurrent = getlong(ole_params->BBD, propCurrent*4); - if (propCurrent < 0 || - propCurrent >= ole_params->fileLength/sectorSize) { - break; - } - } - - ole_params->propNumber = (ole_params->propLen*sectorSize)/PROP_BLOCK_SIZE; - ole_params->propCurNumber = 0; - } else { - ole_finish(ole_params); - ole_params->properties = NULL; - return NULL; - } - - -/* Find Root Entry */ - while ((tEntry=(oleEntry*)ole_readdir(newfile, ole_params)) != NULL) { - if (tEntry->type == oleRootDir) { - ole_params->rootEntry=tEntry; - break; - } - ole_close((FILE*)tEntry); - } - ole_params->propCurNumber = 0; - fseek(newfile, 0, SEEK_SET); - if (!ole_params->rootEntry) { - errno = EINVAL; - ole_finish(ole_params); - return NULL; - } - return newfile; -} - -/** - * - * - * @param oleBuf - * - * @return - */ -int rightOleType(unsigned char *oleBuf) { - return (oleBuf[0x42] == 1 || oleBuf[0x42] == 2 || - oleBuf[0x42] == 3 || oleBuf[0x42] == 5); -} - -/** - * - * - * @param oleBuf - * - * @return - */ -oleType getOleType(unsigned char *oleBuf) { - return (oleType)((unsigned char)oleBuf[0x42]); -} - -/** - * Reads next directory entry from file - * - * @param name buffer for name converted to us-ascii should be at least 33 chars long - * @param size size of file - * - * @return 0 if everything is ok -1 on error - */ -FILE *ole_readdir(FILE *f, struct ole_params_t *ole_params) { - int i, nLen; - unsigned char *oleBuf; - oleEntry *e=NULL; - long int chainMaxLen, chainCurrent; - - if (ole_params->properties == NULL || ole_params->propCurNumber >= ole_params->propNumber || f == NULL) - return NULL; - oleBuf=ole_params->properties + ole_params->propCurNumber*PROP_BLOCK_SIZE; - if (!rightOleType(oleBuf)) - return NULL; - if ((e = (oleEntry*)malloc(sizeof(oleEntry))) == NULL) { - return NULL; - } - e->dirPos=oleBuf; - e->type=getOleType(oleBuf); - e->file=f; - e->startBlock=getlong(oleBuf,0x74); - e->blocks=NULL; - - nLen=getshort(oleBuf,0x40); - for (i=0; i < nLen/2 && i < OLENAMELENGHT; i++) - e->name[i]=(char)oleBuf[i*2]; - e->name[i]='\0'; - (ole_params->propCurNumber)++; - e->length=getulong(oleBuf,0x78); -/* Read sector chain for object */ - chainMaxLen = 25; - e->numOfBlocks = 0; - chainCurrent = e->startBlock; - e->isBigBlock = (e->length >= 0x1000) || !strcmp(e->name, "Root Entry"); -/* fprintf(stderr, "e->name=%s e->length=%ld\n", e->name, e->length); */ -/* fprintf(stderr, "e->startBlock=%ld BBD=%p\n", e->startBlock, BBD); */ - if (e->startBlock >= 0 && - e->length >= 0 && - (e->startBlock <= - ole_params->fileLength/(e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize))) { - if ((e->blocks=malloc(chainMaxLen*sizeof(long int))) == NULL) { - return NULL; - } - while (1) { -/* fprintf(stderr, "chainCurrent=%ld\n", chainCurrent); */ - e->blocks[e->numOfBlocks++] = chainCurrent; - if (e->numOfBlocks >= chainMaxLen) { - long int *newChain; - chainMaxLen+=25; - if ((newChain=realloc(e->blocks, - chainMaxLen*sizeof(long int))) != NULL) - e->blocks=newChain; - else { - free(e->blocks); - e->blocks=NULL; - return NULL; - } - } - if (e->isBigBlock) { - chainCurrent = getlong(ole_params->BBD, chainCurrent*4); - } else if (ole_params->SBD != NULL) { - chainCurrent = getlong(ole_params->SBD, chainCurrent*4); - } else { - chainCurrent=-1; - } - if (chainCurrent <= 0 || - chainCurrent >= (e->isBigBlock ? - ((ole_params->bbdNumBlocks*ole_params->sectorSize)/4) - : ((ole_params->sbdNumber*ole_params->shortSectorSize)/4)) || - (e->numOfBlocks > - e->length/(e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize))) { -/* fprintf(stderr, "chain End=%ld\n", chainCurrent); */ - break; - } - } - } - - if (e->length > (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize)*e->numOfBlocks) - e->length = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize)*e->numOfBlocks; -/* fprintf(stderr, "READDIR: e->name=%s e->numOfBlocks=%ld length=%ld\n", */ -/* e->name, e->numOfBlocks, e->length); */ - - return (FILE*)e; -} - -/** - * Open stream, which correspond to directory entry last read by - * ole_readdir - * - * - * @return opaque pointer to pass to ole_read, casted to (FILE *) - */ -int ole_open(FILE *stream) { - oleEntry *e=(oleEntry *)stream; - if (e->type != oleStream) - return -2; - - e->ole_offset=0; - e->file_offset= ftell(e->file); - return 0; -} - -/** - * - * - * @param e - * @param blk - * - * @return - */ -long int calcFileBlockOffset(oleEntry *e, long int blk, struct ole_params_t *ole_params) { - long int res; - if (e->isBigBlock) { - res=512+e->blocks[blk]*ole_params->sectorSize; - } else { - long int sbdPerSector=(ole_params->sectorSize)/(ole_params->shortSectorSize); - long int sbdSecNum=e->blocks[blk]/sbdPerSector; - long int sbdSecMod=e->blocks[blk]%sbdPerSector; - - res=512 + ole_params->rootEntry->blocks[sbdSecNum]*ole_params->sectorSize + sbdSecMod*ole_params->shortSectorSize; - } - return res; -} - - -/** - * Reads block from open ole stream interface-compatible with fread - * - * @param ptr pointer to buffer for read to - * @param size size of block - * @param nmemb size in blocks - * @param stream pointer to FILE* structure - * - * @return number of readed blocks - */ -size_t ole_read(void *ptr, size_t size, size_t nmemb, FILE *stream, struct ole_params_t *ole_params) { - oleEntry *e = (oleEntry*)stream; - long int llen = size*nmemb, rread=0, i; - long int blockNumber, modBlock, toReadBlocks, toReadBytes, bytesInBlock; - long int ssize; /**< Size of block */ - long int newoffset; - unsigned char *cptr = ptr; - if (e->ole_offset+llen > e->length) - llen= e->length - e->ole_offset; - - ssize = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize); - blockNumber=e->ole_offset/ssize; -/* fprintf(stderr, "blockNumber=%ld e->numOfBlocks=%ld llen=%ld\n", */ -/* blockNumber, e->numOfBlocks, llen); */ - if (blockNumber >= e->numOfBlocks || llen <=0) - return 0; - - modBlock=e->ole_offset%ssize; - bytesInBlock = ssize - modBlock; - if (bytesInBlock < llen) { - toReadBlocks = (llen-bytesInBlock)/ssize; - toReadBytes = (llen-bytesInBlock)%ssize; - } else { - toReadBlocks = toReadBytes = 0; - } -/* fprintf(stderr, "llen=%ld toReadBlocks=%ld toReadBytes=%ld bytesInBlock=%ld blockNumber=%ld modBlock=%ld\n", */ -/* llen, toReadBlocks, toReadBytes, bytesInBlock, blockNumber, modBlock); */ - newoffset = calcFileBlockOffset(e,blockNumber, ole_params)+modBlock; - if (e->file_offset != newoffset) { - fseek(e->file, e->file_offset=newoffset, SEEK_SET); - } - rread=fread(ptr, 1, min(llen,bytesInBlock), e->file); - e->file_offset += rread; - for (i=0; ifile_offset); - fseek(e->file, e->file_offset=newoffset, SEEK_SET); - readbytes=fread(cptr+rread, 1, min(llen-rread, ssize), e->file); - rread +=readbytes; - e->file_offset +=readbytes; - } - if (toReadBytes > 0) { - int readbytes; - blockNumber++; - newoffset = calcFileBlockOffset(e,blockNumber, ole_params); - fseek(e->file, e->file_offset=newoffset, SEEK_SET); - readbytes=fread(cptr+rread, 1, toReadBytes,e->file); - rread +=readbytes; - e->file_offset +=readbytes; - } -/* fprintf(stderr, "ole_offset=%ld rread=%ld llen=%ld\n", - e->ole_offset, rread, llen);*/ - e->ole_offset+=rread; - return rread; -} - -/** - * - * - * @param stream - * - * @return - */ -int ole_eof(FILE *stream) { - oleEntry *e=(oleEntry*)stream; -/* fprintf(stderr, "EOF: e->ole_offset=%ld e->length=%ld\n", - e->ole_offset, e->length);*/ - return (e->ole_offset >= e->length); -} - -/** - * - * - */ -void ole_finish(struct ole_params_t *ole_params) { - if (ole_params->BBD != NULL) free(ole_params->BBD); - if (ole_params->SBD != NULL) free(ole_params->SBD); - if (ole_params->properties != NULL) free(ole_params->properties); - if (ole_params->rootEntry != NULL) ole_close((FILE*)(ole_params->rootEntry)); - ole_params->properties = ole_params->SBD = ole_params->BBD = NULL; - ole_params->rootEntry = NULL; -} - -/** - * - * - * @param stream - * - * @return - */ -int ole_close(FILE *stream) { - oleEntry *e=(oleEntry*)stream; - if (e == NULL) - return -1; - if (e->blocks != NULL) - free(e->blocks); - free(e); - return 0; -} - -/** - * - * - * @param stream pointer to OLE stream structure - * @param offset - * @param whence - * - * @return - */ -int ole_seek(FILE *stream, long offset, int whence, struct ole_params_t *ole_params) { - oleEntry *e=(oleEntry*)stream; - long int new_ole_offset=0, new_file_offset; - int ssize, modBlock, blockNumber; - - switch (whence) { - case SEEK_SET: - new_ole_offset=offset; - break; - - case SEEK_CUR: - new_ole_offset=e->ole_offset+offset; - break; - - case SEEK_END: - new_ole_offset=e->length+offset; - break; - - default: - errno=EINVAL; - return -1; - } - if (new_ole_offset<0) - new_ole_offset=0; - if (new_ole_offset >= e->length) - new_ole_offset=e->length; - - ssize = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize); - blockNumber=new_ole_offset/ssize; - if (blockNumber >= e->numOfBlocks) - return -1; - - modBlock=new_ole_offset%ssize; - new_file_offset = calcFileBlockOffset(e,blockNumber, ole_params)+modBlock; - fseek(e->file, e->file_offset=new_file_offset, SEEK_SET); - e->ole_offset=new_ole_offset; - - return 0; -} - -/** - * Tell position inside OLE stream - * - * @param stream pointer to OLE stream - * - * @return current position inside OLE stream - */ -long ole_tell(FILE *stream) { - oleEntry *e=(oleEntry*)stream; - return e->ole_offset; -} - - -void set_ole_func(struct io_funcs_t *io_funcs) { - io_funcs->catdoc_read=ole_read; - io_funcs->catdoc_eof=ole_eof; - io_funcs->catdoc_seek=ole_seek; - io_funcs->catdoc_tell=ole_tell; -} - - -size_t my_fread(void *ptr, size_t size, size_t nmemb, FILE *stream, struct ole_params_t *ole_params) -{ - return fread(ptr, size, nmemb, stream); -} - -int my_fseek(FILE *stream, long offset, int whence, struct ole_params_t *ole_params) -{ - return fseek(stream, offset, whence); -} - -void set_std_func(struct io_funcs_t *io_funcs) { - io_funcs->catdoc_read=my_fread; - io_funcs->catdoc_eof=feof; - io_funcs->catdoc_seek=my_fseek; - io_funcs->catdoc_tell=ftell; -} diff --git a/test/bug-hunting/itc.py b/test/bug-hunting/itc.py deleted file mode 100644 index c4c3f856793..00000000000 --- a/test/bug-hunting/itc.py +++ /dev/null @@ -1,91 +0,0 @@ -# Test if --bug-hunting works using the itc testsuite -# The itc test suite can be downloaded here: -# https://github.com/regehr/itc-benchmarks - - -import os -import re -import shutil -import sys -import subprocess - -if sys.argv[0] in ('test/bug-hunting/itc.py', './test/bug-hunting/itc.py'): - CPPCHECK_PATH = './cppcheck' -else: - CPPCHECK_PATH = '../../cppcheck' - -if len(sys.argv) >= 2 and sys.argv[-1] != '--clang': - TESTFILES = [sys.argv[-1]] -else: - TESTFILES = [os.path.expanduser('~/itc/01.w_Defects/zero_division.c'), - os.path.expanduser('~/itc/01.w_Defects/uninit_var.c')] -if not os.path.isfile(TESTFILES[0]): - print('ERROR: %s is not a file' % TESTFILES[0]) - sys.exit(1) - -RUN_CLANG = ('--clang' in sys.argv) - -def get_error_lines(filename): - ret = [] - f = open(filename, 'rt') - lines = f.readlines() - for linenr, line in enumerate(lines): - if line.find('/* ERROR:') > 0 or line.find('/*ERROR:') > 0: - linenr += 1 - if testfile.find('uninit_') >= 0: - if linenr == 177: - linenr = 176 - elif linenr == 241: - linenr = 242 # warn about usage - ret.append(linenr) - return ret - -def check(filename): - cmd = [CPPCHECK_PATH, - '--bug-hunting', - '--bug-hunting-check-function-max-time=10' - '--platform=unix64', - filename] - if RUN_CLANG: - cmd.append('--clang') - print(' '.join(cmd)) - - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - comm = p.communicate() - stdout = comm[0].decode(encoding='utf-8', errors='ignore') - stderr = comm[1].decode(encoding='utf-8', errors='ignore') - - if RUN_CLANG: - shutil.rmtree('itc-build-dir') - - if filename.find('zero_division.c') >= 0: - w = r'.*zero_division.c:([0-9]+):[0-9]+: error: There is division.*' - elif filename.find('uninit_') >= 0: - w = r'.*c:([0-9]+):[0-9]+: error: .*bughuntingUninit.*' - else: - w = r'.*c:([0-9]+):[0-9]+: error: .*bughunting.*' - - ret = [] - for line in stderr.split('\n'): - res = re.match(w, line) - if res is None: - continue - linenr = int(res.group(1)) - if linenr not in ret: - ret.append(linenr) - return ret - -for testfile in TESTFILES: - wanted = get_error_lines(testfile) - actual = check(testfile) - missing = [] - for w in wanted: - if w not in actual: - missing.append(w) - if len(missing) > 0: - print('wanted:' + str(wanted)) - print('actual:' + str(actual)) - print('missing:' + str(missing)) - sys.exit(1) - - diff --git a/test/bug-hunting/juliet.py b/test/bug-hunting/juliet.py deleted file mode 100644 index b0a7a0d2c19..00000000000 --- a/test/bug-hunting/juliet.py +++ /dev/null @@ -1,77 +0,0 @@ -# Test if --bug-hunting works using the juliet testsuite -# The Juliet test suite can be downloaded from: -# https://samate.nist.gov/SRD/testsuite.php - -import glob -import os -import re -import shutil -import sys -import subprocess - -JULIET_PATH = os.path.expanduser('~/juliet') -if sys.argv[0] in ('test/bug-hunting/juliet.py', './test/bug-hunting/juliet.py'): - CPPCHECK_PATH = './cppcheck' -else: - CPPCHECK_PATH = '../../cppcheck' - -RUN_CLANG = ('--clang' in sys.argv) - -def get_files(juliet_path:str, test_cases:str): - ret = [] - g = os.path.join(juliet_path, test_cases) - print(g) - for f in sorted(glob.glob(g)): - res = re.match(r'(.*[0-9][0-9])[a-x]?.(cp*)$', f) - if res is None: - print('Non-match!! ' + f) - sys.exit(1) - f = res.group(1) + '*.' + res.group(2) - if f not in ret: - ret.append(f) - return ret - - -def check(tc:str, warning_id:str): - num_ok = 0 - num_failed = 0 - - for f in get_files(JULIET_PATH, tc): - cmd = [CPPCHECK_PATH, - '-I' + os.path.join(JULIET_PATH, 'C/testcasesupport'), - '-DOMIT_GOOD', - '-DAF_INET=1', - '-DINADDR_ANY=1', - '--library=posix', - '--bug-hunting', - '--platform=unix64'] - if RUN_CLANG: - cmd += ['--clang', '--cppcheck-build-dir=juliet-build-dir'] - if not os.path.isdir('juliet-build-dir'): - os.mkdir('juliet-build-dir') - cmd += glob.glob(f) - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - comm = p.communicate() - stdout = comm[0].decode(encoding='utf-8', errors='ignore') - stderr = comm[1].decode(encoding='utf-8', errors='ignore') - if RUN_CLANG: - shutil.rmtree('juliet-build-dir') - - if warning_id in stderr: - num_ok += 1 - else: - print('fail: ' + ' '.join(cmd)) - num_failed += 1 - - cwepos = tc.find('CWE') - cwe = tc[cwepos:cwepos+6] - - print('%s ok:%i, fail:%i' % (cwe, num_ok, num_failed)) - if num_failed != 0: - sys.exit(1) - - -check('C/testcases/CWE369_Divide_by_Zero/s*/*.c', 'bughuntingDivByZero') -#check('C/testcases/CWE457_Use_of_Uninitialized_Variable/s*/*.c', 'bughuntingUninit') - - diff --git a/test/synthetic/Makefile b/test/synthetic/Makefile deleted file mode 100644 index 11afb4df579..00000000000 --- a/test/synthetic/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -ifndef CC - CC=gcc -endif - -all: controlflow.o data.o functions.o ub.o - -controlflow.o: controlflow.c - $(CC) -c controlflow.c - -data.o: data.c - $(CC) -c data.c - -functions.o: functions.c - $(CC) -c functions.c - -ub.o: ub.c - $(CC) -c ub.c - -clean: - rm -rf controlflow.o data.o functions.o ub.o diff --git a/test/synthetic/controlflow.c b/test/synthetic/controlflow.c deleted file mode 100644 index c92106df0c2..00000000000 --- a/test/synthetic/controlflow.c +++ /dev/null @@ -1,73 +0,0 @@ - -////////////////////////////// -// control flow analysis -////////////////////////////// - -int buf[2]; - -void in_if(int a) { - if (a==100) - buf[a] = 0; // BUG -} - -void before_if(int a) { - buf[a] = 0; // WARNING - if (a==100) {} -} - -void after_if(int a) { - if (a==100) {} - buf[a] = 0; // WARNING -} - -void in_for(void) { - int x; - for (x = 0; x<100; x++) { - buf[x] = 0; // BUG - } -} - -void after_for(void) { - int x; - for (x = 0; x<100; x++) {} - buf[x] = 0; // BUG -} - -void in_switch(int x) { - switch (x) { - case 100: - buf[x] = 0; // BUG - break; - } -} - -void before_switch(int x) { - buf[x] = 0; // WARNING - switch (x) { - case 100: - break; - } -} - -void after_switch(int x) { - switch (x) { - case 100: - break; - } - buf[x] = 0; // WARNING -} - -void in_while(void) { - int x = 0; - while (x<100) { - buf[x] = 0; // BUG - x++; - } -} - -void after_while(void) { - int x = 0; - while (x<100) - x++; - buf[x] = 0; // BUG -} diff --git a/test/synthetic/data.c b/test/synthetic/data.c deleted file mode 100644 index 9d2a5d578f4..00000000000 --- a/test/synthetic/data.c +++ /dev/null @@ -1,68 +0,0 @@ - -int TestData[10]; - -int g; -void global() { - g = 1000; - TestData[g] = 0; // BUG -} - -int garr[10]; -void global_array() { - garr[3] = 1000; - TestData[garr[3]] = 0; // BUG -} - -int *gp; -void global_pointer() { - *gp = 1000; - TestData[*gp] = 0; // BUG -} - - -void local() { - int x; - x = 1000; - TestData[x] = 0; // BUG -} - -void local_array() { - int arr[10]; - arr[3] = 1000; - TestData[arr[3]] = 0; // BUG -} - -void local_alias_1() { - int x; - int *p = &x; - *p = 1000; - TestData[*p] = 0; // BUG -} - -void local_alias_2() { - int x; - int *p = &x; - x = 1000; - TestData[*p] = 0; // BUG -} - -struct ABC { - int a; - int b[10]; - int c; -}; - -void struct_member_init() { - struct ABC abc = {1000,{0},3}; - TestData[abc.a] = 0; // BUG -} - -void struct_member_assign(struct ABC *abc) { - abc->a = 1000; - TestData[abc->a] = 0; // BUG -} - -void struct_arraymember(struct ABC *abc) { - abc->b[3] = 1000; - TestData[abc->b[3]] = 0; // BUG -} diff --git a/test/synthetic/functions.c b/test/synthetic/functions.c deleted file mode 100644 index bc6639cb9f0..00000000000 --- a/test/synthetic/functions.c +++ /dev/null @@ -1,24 +0,0 @@ - -int TestData[100]; - - -void par_not_dependant(int par) { - TestData[par] = 0; // BUG -} -void par_dependant(int x, int y) { - if (x < 10) - TestData[y] = 0; // BUG -} -void call(int x) { - par_not_dependant(1000); - par_dependant(0, 1000); -} - -int getLargeIndex() { - return 1000; -} -void return_value() { - TestData[getLargeIndex()] = 0; // BUG -} - - diff --git a/test/synthetic/report.py b/test/synthetic/report.py deleted file mode 100755 index 17f58239f9e..00000000000 --- a/test/synthetic/report.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python3 -import os -import re - - -def hasresult(filename, result): - if not os.path.isfile(filename): - return False - for line in open(filename, 'rt'): - if result in line: - return True - return False - - -def parsefile(filename): - ret = [] - linenr = 0 - functionName = None - for line in open(filename, 'rt'): - linenr = linenr + 1 - res = re.match('^[a-z]+[ *]+([a-z0-9_]+)[(]', line) - if res: - functionName = res.group(1) - if line.startswith('}'): - functionName = '' - elif 'BUG' in line or 'WARN' in line or filename == 'ub.c': - spaces = ' ' * 100 - s = filename + spaces - s = s[:15] + str(linenr) + spaces - s = s[:20] + functionName + spaces - s = s[:50] - if hasresult('cppcheck.txt', '[' + filename + ':' + str(linenr) + ']'): - s = s + ' X' - else: - s = s + ' ' - if hasresult('clang.txt', filename + ':' + str(linenr)): - s = s + ' X' - else: - s = s + ' ' - if hasresult('lint.txt', filename + ' ' + str(linenr)): - s = s + ' X' - else: - s = s + ' ' - if hasresult('cov.txt', filename + ':' + str(linenr)): - s = s + ' X' - else: - s = s + ' ' - ret.append(s) - return ret - -bugs = [] -bugs.extend(parsefile('controlflow.c')) -bugs.extend(parsefile('data.c')) -bugs.extend(parsefile('functions.c')) -bugs.extend(parsefile('ub.c')) -for bug in bugs: - print(bug) diff --git a/test/synthetic/run-clang.sh b/test/synthetic/run-clang.sh deleted file mode 100755 index 3adf2600a1e..00000000000 --- a/test/synthetic/run-clang.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -~/llvm/build/bin/clang -cc1 -analyze -analyzer-checker=alpha.security controlflow.c data.c functions.c 2>&1 /dev/null | grep warning -~/llvm/build/bin/clang -cc1 -analyze -analyzer-checker=alpha.security,core ub.c 2>&1 /dev/null | grep warning - diff --git a/test/synthetic/run-lint.bat b/test/synthetic/run-lint.bat deleted file mode 100755 index e578df1a6b7..00000000000 --- a/test/synthetic/run-lint.bat +++ /dev/null @@ -1,4 +0,0 @@ -\lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 controlflow.c -\lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 data.c -\lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 functions.c -\lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 ub.c diff --git a/test/synthetic/ub.c b/test/synthetic/ub.c deleted file mode 100644 index 1ded7b524da..00000000000 --- a/test/synthetic/ub.c +++ /dev/null @@ -1,49 +0,0 @@ -void alias() { - int x; int *ip=&x; float *fp = (float *)ip; -} -int buffer_overflow() { - int x[10]={0}; return x[100]; -} -int dead_pointer(int a) { - int *p=&a; if (a) { int x=0; p = &x; } return *p; -} -int division_by_zero() { - return 100 / 0; -} -int float_to_int() { - double d=1E100; return (int)d; -} -void negative_size(int sz) { - if (sz < 0) { int buf[sz]; } -} -int no_return() {} -int null_pointer() { - int *p = 0; return *p; -} -int *pointer_arithmetic() { - static int buf[10]; return buf + 100; -} -unsigned char pointer_to_u8() { - static int buf[10]; return (int*)buf; -} -int pointer_subtraction() { - char a[10]; char b[10]; return b-a; -} -int pointer_comparison() { - char a[10]; char b[10]; return b> 1; return intmax * 2; -} -void string_literal() { - *((char *)"hello") = 0; -} -int uninit() { - int x; return x + 2; -} diff --git a/test/testsuites/clang/outofbound.c b/test/testsuites/clang/outofbound.c deleted file mode 100644 index 8be946474b3..00000000000 --- a/test/testsuites/clang/outofbound.c +++ /dev/null @@ -1,128 +0,0 @@ -// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-store=region -verify %s \ -// RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=unix \ -// RUN: -analyzer-checker=alpha.security.ArrayBound \ -// RUN: -analyzer-config unix.DynamicMemoryModeling:Optimistic=true - -typedef __typeof(sizeof(int)) size_t; -void *malloc(size_t); -void *calloc(size_t, size_t); - -char f1() { - char* s = "abcd"; - char c = s[4]; // no-warning - return s[5] + c; // expected-warning{{Access out-of-bound array element (buffer overflow)}} -} - -void f2() { - int *p = malloc(12); - p[3] = 4; // expected-warning{{Access out-of-bound array element (buffer overflow)}} -} - -struct three_words { - int c[3]; -}; - -struct seven_words { - int c[7]; -}; - -void f3() { - struct three_words a, *p; - p = &a; - p[0] = a; // no-warning - p[1] = a; // expected-warning{{Access out-of-bound array element (buffer overflow)}} -} - -void f4() { - struct seven_words c; - struct three_words a, *p = (struct three_words *)&c; - p[0] = a; // no-warning - p[1] = a; // no-warning - p[2] = a; // expected-warning{{Access out-of-bound array element (buffer overflow)}} -} - -void f5() { - char *p = calloc(2,2); - p[3] = '.'; // no-warning - p[4] = '!'; // expected-warning{{out-of-bound}} -} - -void f6() { - char a[2]; - int *b = (int*)a; - b[1] = 3; // expected-warning{{out-of-bound}} -} - -void f7() { - struct three_words a; - a.c[3] = 1; // expected-warning{{out-of-bound}} -} - -void vla(int a) { - if (a == 5) { - int x[a]; - x[4] = 4; // no-warning - x[5] = 5; // expected-warning{{out-of-bound}} - } -} - -void alloca_region(int a) { - if (a == 5) { - char *x = __builtin_alloca(a); - x[4] = 4; // no-warning - x[5] = 5; // expected-warning{{out-of-bound}} - } -} - -int symbolic_index(int a) { - int x[2] = {1, 2}; - if (a == 2) { - return x[a]; // expected-warning{{out-of-bound}} - } - return 0; -} - -int symbolic_index2(int a) { - int x[2] = {1, 2}; - if (a < 0) { - return x[a]; // expected-warning{{out-of-bound}} - } - return 0; -} - -int overflow_binary_search(double in) { - int eee = 16; - if (in < 1e-8 || in > 1e23) { - return 0; - } else { - static const double ins[] = {1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, - 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, - 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; - if (in < ins[eee]) { - eee -= 8; - } else { - eee += 8; - } - if (in < ins[eee]) { - eee -= 4; - } else { - eee += 4; - } - if (in < ins[eee]) { - eee -= 2; - } else { - eee += 2; - } - if (in < ins[eee]) { - eee -= 1; - } else { - eee += 1; - } - if (in < ins[eee]) { // expected-warning {{Access out-of-bound array element (buffer overflow)}} - eee -= 1; - } - } - return eee; -} diff --git a/test/testsuites/clang/readme.txt b/test/testsuites/clang/readme.txt deleted file mode 100644 index aac49ec20e4..00000000000 --- a/test/testsuites/clang/readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -arrayIndexOutOfBounds: -~/llvm/tools/clang/test/Analysis/outofbound.c - diff --git a/test/testsuites/danmar/divbyzero.cpp b/test/testsuites/danmar/divbyzero.cpp deleted file mode 100644 index ffd7b7bb4d2..00000000000 --- a/test/testsuites/danmar/divbyzero.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// make USE_Z3=yes -// ./cppcheck --verify --inline-suppr --enable=information test/testsuites/danmar-verify/divbyzero.cpp - -#include -#include - -struct S { int x; }; - -int globalvar; - -void dostuff(); - -int callfunc1() { - int x = 16; - scanf("%i\n", &x); - // cppcheck-suppress verificationDivByZero - return 100000 / x; -} - -int float1(float f) { - // cppcheck-suppress verificationDivByZero - return 100000 / (int)f; -} - -float float2(float f) { - // cppcheck-suppress verificationDivByZeroFloat - return 100000 / f; -} - -int functionCall() { -#ifdef __clang__ - return 0; -#else - // cppcheck-suppress verificationDivByZero - return 100000 / unknown_function(); -#endif -} - -int globalVar1() { - // cppcheck-suppress verificationDivByZero - return 100000 / globalvar; -} - -int globalVar2() { - globalvar = 123; - dostuff(); - // cppcheck-suppress verificationDivByZero - return 100000 / globalvar; -} - -int pointer1(int *p) { - // cppcheck-suppress verificationDivByZero - return 100000 / *p; -} - -int pointer2(int *p) { - // cppcheck-suppress verificationDivByZero - return 100000 / p[32]; -} - -int stdmap(std::map &data) { - // cppcheck-suppress verificationDivByZero - return 100000 / data[43]; -} - -int struct1(struct S *s) { - // cppcheck-suppress verificationDivByZero - return 100000 / s->x; -} - -int trycatch() { - int x = 0; - try { - dostuff(); - x = 1; - } catch (...) {} - // cppcheck-suppress verificationDivByZero - return 100000 / x; -} diff --git a/test/testsuites/danmar/uninit.c b/test/testsuites/danmar/uninit.c deleted file mode 100644 index ba3178099c9..00000000000 --- a/test/testsuites/danmar/uninit.c +++ /dev/null @@ -1,37 +0,0 @@ - -// make USE_Z3=yes -// ./cppcheck --verify --inline-suppr --enable=information test/testsuites/danmar-verify/uninit.c - -#include - -int array1() { - int a[10]; - a[0] = 0; - // cppcheck-suppress verificationUninit - return a[2]; -} - -int array2() { - int a[10][10]; - a[0][0] = 0; - // cppcheck-suppress verificationUninit - return a[2][3]; -} - -int local1() { - int x; - // cppcheck-suppress verificationUninit - // cppcheck-suppress uninitvar - return x; -} - -int pointer1(int *p) { - // cppcheck-suppress verificationUninit - return *p; -} - -int pointer2(char *p) { - // cppcheck-suppress verificationUninitArg - return strlen(p); -} - diff --git a/test/testsuites/duma/leak1.c b/test/testsuites/duma/leak1.c deleted file mode 100644 index 312a016efe0..00000000000 --- a/test/testsuites/duma/leak1.c +++ /dev/null @@ -1,12 +0,0 @@ -#include - -int main() { - printf("Hello world!\n"); - - int* pI; - pI = (int*)malloc(sizeof(int)); - printf("Let's leak a pointer to int\n"); - *pI = 303; - - return 0; -} diff --git a/test/testsuites/duma/leak1.cc b/test/testsuites/duma/leak1.cc deleted file mode 100644 index 6f97dbdd908..00000000000 --- a/test/testsuites/duma/leak1.cc +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include - -using namespace std; - -int main() { - cout << "Hello world!" << endl; - - int* pI = new int; - cerr << "Let's leak a pointer to int" << endl; - *pI = 303; - - return 0; -} diff --git a/test/testsuites/duma/leak2.c b/test/testsuites/duma/leak2.c deleted file mode 100644 index 0fa6cb03d83..00000000000 --- a/test/testsuites/duma/leak2.c +++ /dev/null @@ -1,20 +0,0 @@ -#include - -int main() { - printf("Hello world!\n"); - - int* pI; - pI = (int*)malloc(10*sizeof(int)); - - printf("Let's leak a pointer to an array of 10 ints\n"); - int i=0; - for (i=0; i<9; i++) { - pI[i] = 303+i; - } - int j=0; - for (j=0; j<9; j++) { - if (pI[j] != 303+j) printf(" Something strange is happening...\n"); - } - - return 0; -} diff --git a/test/testsuites/duma/leak2.cc b/test/testsuites/duma/leak2.cc deleted file mode 100644 index e71db91d5ca..00000000000 --- a/test/testsuites/duma/leak2.cc +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include - -using namespace std; - -int main() { - cout << "Hello world!" << endl; - - int* pI = new int[10]; - cerr << "Let's leak a pointer to an array of 10 ints" << endl; - for (int i=0; i<9; i++) { - pI[i] = 303+i; - } - for (int i=0; i<9; i++) { - if (pI[i] != 303+i) cerr << " Something strange is happening..." << endl; - } - - return 0; -} diff --git a/test/testsuites/duma/memCheckers.html b/test/testsuites/duma/memCheckers.html deleted file mode 100644 index 58bc149b91d..00000000000 --- a/test/testsuites/duma/memCheckers.html +++ /dev/null @@ -1,367 +0,0 @@ - -Comparison of Free Memory Checkers - - - - - - - - - -

Jean-Philippe Martin | -Resources | Memory Checkers Comparison

-
-

- -

Memory Checkers

- -Memory checkers are debugging tools that help programmers find -improper use of pointers, typically memory leaks. - -

-There are some freely available memory checkers. I ran a series of -very simple tests to determine what they can do. The wrong -series of tests contains code that makes pointer mistakes that are not -memory leaks, for example freeing a pointer twice, writing to -uninitialized memory or using delete instead of delete []. The -leak series of tests contains simple memory leaks, -i.e. pointers that are allocated but not released. The ok -series of tests contains programs that are correct and thus should not -cause the memory checker to output any alarm message. -

- -

C tests

- -(updated 3/10/2006)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Checker - - wrong1.c - wrong3.c - wrong6.c - wrong7.c - leak1.c - leak2.c - ok5.c - -
MALLOC_CHECK_ - - OK - missed - missed - OK* - missed - missed - OK -
dmalloc - OK - missed - missed - OK - OK - OK - OK -
memCheckDeluxe - - missed - missed - missed - missed - OK - OK - OK -
memwatch - - OK - missed - missed* - OK - OK - OK - OK -
DUMA - OK - - missed - missed - - OK - OK - OK* - OK -
valgrind - - OK - OK - missed - OK - OK - OK - OK -
- -

- -

C++ tests

- - - - - - - - - - - - - - - - - - - -
Checker - - wrong1.cc - wrong2.cc - wrong3.cc - wrong4.cc - wrong5.cc - wrong6.cc - leak1.cc - leak2.cc - ok5.cc - -
MALLOC_CHECK_ - - OK - OK - missed - missed - OK - missed - missed - missed - OK -
dmalloc - OK - OK* - missed* - missed - OK - missed - OK - OK - missed* -
DUMA - OK - OK - - missed - OK - OK - missed - OK - OK - missed -
valgrind - - OK - OK - OK* - OK - OK - missed - OK - OK - OK -
-

- -

Conclusion

- -

-memWatch and memCheckDeluxe are both memory leak detectors, and they -passed all the memory leak tests. Memwatch wins this round because it -was able to detect the double-free in wrong1.c and the out-of-bounds -accesses in the dynamically allocated array of wrong7.c (not the -static array of wrong6 - but no one else did, either). -Both programs are designed to work with C and require a -recompilation. -

- -

-MALLOC_CHECK_ is an interesting test: it is triggered simply by -setting the environment variable MALLOC_CHECK_ to 1, and the rest of -the magic is done by glibc (see the link in references, below). This -is the easiest check to set up and it requires no recompilation. It -detected the double free in wrong1 and the mismatched malloc/delete or -new/free pairs in wrong2.cc and wrong5.cc. It was able to see that -something was fishy in wrong7.c, but it reports a single error at the "free" -instead of when we are accessing the memory instead of two errors, for -each out-of-bounds access. MALLOC_CHECK_ cannot detect -memory leaks and did not detect the use of uninitialized memory in -wrong3. -

- -

-dmalloc is more than a leak detector, but it didn't detect as -many bad cases as valgrind and requires a recompile. Also, its C++ -support is (in the author's words) minimal. In particular, I have not -been able to get dmalloc to report line numbers with C++ (log), although that feature mostly -works with C code - in both leak1.c and leak2.c it pointed to -the return() instead of the line that allocated the unfreed -memory. Dmalloc also often reports unfreed memory, even for programs -that are correct. This may be because of errors in the c++ library, -but it makes the reports harder to read. In contrast, valgrind has a -way to hide leaks that it knows about so its reports are more -clear. See also the author's comments. -

- -

-valgrind is clearly the winner of this little contest. valgrind -requires no recompilation of the program, so it's very easy to set -up. It identified almost all of the incorrect pointer uses and memory -leaks. The only test that it missed is wrong6, in which we break the -bounds of an array. No other checker spotted that one, though. Also, -valgrind has been improved since we ran this test, so it may perform -even better than what we show here. -

- -

-DUMA is a very close second. The results I am posting here come from -Koneru Srikanth (kpsrikanth at gmail dot com) who generously sent them -to me. DUMA seems not to require a recompile, but the tests were run -on recompiled code. DUMA performs really well. It was also able to -detect out-of-bounds writes -(it is reported as failing wrong3.cc because it missed the -out-of-bounds read). If for some reason valgrind does not work for -you, then I recommend that you give DUMA a spin. - - -

Reference

- -I tested: - -
    -
  • MALLOC_CHECK_ - for glibc (C and C++: requires no recompilation) -
  • dmalloc-5.2.2 (C, minimal C++ support; requires recompilation) -
  • memCheckDeluxe-1.2.2 (C, -some C++. Requires recompilation) -
  • memwatch-2.71 (C -only; requires recompilation) -
  • valgrind-1.9.6 - (C, C++ and more: requires no recompilation) -
  • DUMA version 2.4.26 - (C and C++. Documentation says that no recompilation is needed, but - the tests were run on recompiled code) (as mentioned above, these tests - were contributed by Koneru Srikanth). -
- -I did not test: - - - -Test programs: - - - -

ToDo

- -The following memory checkers have been mentioned to me but I haven't -tried them yet: -
    -
  • mpatrol at http://www.cbmamiga.demon.co.uk/mpatrol/ -
- -

Change History

- -March 10, 2006: added DUMA, contributed by Koneru Srikanth -
-Oct 6, 2003: mention of mpatrol -
-Sept 29, 2003: added dmalloc -
-June 25, 2003: minor change in the text -
-June 24, 2003: corrected result for memwatch's wrong1.c, added wrong7.c -
-June 15, 2003: initial release -

- -Please contact me if you have feedback or -would like to suggest another tool for the test. - -


- [JP Martin] - [resources] - [contact information] -

-

-Best viewed with *any* browser -
- - \ No newline at end of file diff --git a/test/testsuites/duma/wrong1.c b/test/testsuites/duma/wrong1.c deleted file mode 100644 index 196360da616..00000000000 --- a/test/testsuites/duma/wrong1.c +++ /dev/null @@ -1,13 +0,0 @@ -#include - -int main() { - printf("Hello world!\n"); - - int* pI = (int*)malloc(sizeof(int)); - *pI=2; - free(pI); - printf("Now freeing a pointer twice...\n"); - free(pI); - printf("Did you notice?\n"); - return 0; -} diff --git a/test/testsuites/duma/wrong1.cc b/test/testsuites/duma/wrong1.cc deleted file mode 100644 index ecb9c60852d..00000000000 --- a/test/testsuites/duma/wrong1.cc +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include -#include - -using namespace std; - -int main() { - cout << "Hello world!" << endl; - int* pI = new int; - *pI=2; - delete(pI); - cerr << "Now deleting a pointer twice..." << endl; - delete(pI); - cerr << "Did you notice?" << endl; - return 0; -} diff --git a/test/testsuites/duma/wrong2.cc b/test/testsuites/duma/wrong2.cc deleted file mode 100644 index 2c7786a297d..00000000000 --- a/test/testsuites/duma/wrong2.cc +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include - -using namespace std; - -int main() { - cout << "Hello world!" << endl; - - int* pI = new int; - *pI=2; - cerr << "Now freeing a pointer instead of deleting it..." << endl; - free(pI); - cerr << "Did you notice?" << endl; - - - pI = new int; - delete(pI); - cerr << "Now deleting twice..." << endl; - delete(pI); - cerr << "Did you notice?" << endl; - - cerr << "There should be 2 errors in this run" << endl; - return 0; -} diff --git a/test/testsuites/duma/wrong3.c b/test/testsuites/duma/wrong3.c deleted file mode 100644 index fd0bbf26951..00000000000 --- a/test/testsuites/duma/wrong3.c +++ /dev/null @@ -1,22 +0,0 @@ -#include - -int main() { - printf("Hello world!\n"); - - int* pI = (int*)malloc(sizeof(int)); - int j; - printf("Now reading uninitialized memory\n"); - j = *pI+2; - printf("Did you notice? (value was %i)\n",j); - free(pI); - printf("(No memory leak here)\n"); - - int* pJ; - printf("Now writing to uninitialized pointer\n"); - *pJ = j; - printf("Did you notice?\n"); - - // valgrind reports 8, but that's ok - printf("There should be 2 errors in this run\n"); - return 0; -} diff --git a/test/testsuites/duma/wrong3.cc b/test/testsuites/duma/wrong3.cc deleted file mode 100644 index 309b38e0ab7..00000000000 --- a/test/testsuites/duma/wrong3.cc +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include - -using namespace std; - -int main() { - cout << "Hello world!" << endl; - - int* pI = new int; - int j; - cerr << "Now reading uninitialized memory" << endl; - j = *pI+2; - cerr << "Did you notice? (value was " << j << ") " << endl; - delete pI; - cerr << "(No memory leak here)" << endl; - - int* pJ; - cerr << "Now writing to uninitialized pointer" << endl; - *pJ = j; - cerr << "Did you notice?" << endl; - - // valgrind reports 4, but that's ok - cerr << "There should be 2 errors in this run" << endl; - return 0; -} diff --git a/test/testsuites/duma/wrong4.cc b/test/testsuites/duma/wrong4.cc deleted file mode 100644 index 2e83c9b52a5..00000000000 --- a/test/testsuites/duma/wrong4.cc +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include - -using namespace std; - -int main() { - cout << "Hello world!" << endl; - - { - int* pI = new int[10]; - cerr << "Let's delete instead of delete [] " << endl; - delete pI; - cerr << "Did you notice?" << endl; - } - - { - int* pI = new int[10]; - cerr << "Now let's free instead of delete [] " << endl; - free(pI); - cerr << "Did you notice?" << endl; - } - - cerr << "There should be 2 errors in this run" << endl; - return 0; -} diff --git a/test/testsuites/duma/wrong5.cc b/test/testsuites/duma/wrong5.cc deleted file mode 100644 index 50c6b688534..00000000000 --- a/test/testsuites/duma/wrong5.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include -#include - -using namespace std; - -class Test { -public: - int a; - string stdstr; - - Test() { - a=2; - stdstr = "test"; - } - -}; - -int main() { - cout << "Hello world!" << endl; - - { - Test* pI = new Test[10]; - cerr << "Let's delete instead of delete [] " << endl; - delete pI; - cerr << "Did you notice?" << endl; - } - - { - Test* pI = new Test[10]; - cerr << "Now let's free instead of delete [] " << endl; - free(pI); - cerr << "Did you notice?" << endl; - } - - cerr << "There should be 2 errors in this run" << endl; - return 0; -} diff --git a/test/testsuites/duma/wrong6.c b/test/testsuites/duma/wrong6.c deleted file mode 100644 index 4666c07dddd..00000000000 --- a/test/testsuites/duma/wrong6.c +++ /dev/null @@ -1,19 +0,0 @@ -#include - -struct Test { - int a; - char st[10]; -}; - -int main() { - printf("Hello world!\n"); - - struct Test ar[10]; - struct Test b; - printf("Let's index out of bounds \n"); - ar[10].a=10; - printf("Did you notice?\n"); - - printf("There should be 1 error in this run\n"); - return 0; -} diff --git a/test/testsuites/duma/wrong6.cc b/test/testsuites/duma/wrong6.cc deleted file mode 100644 index 85dd1ffefaf..00000000000 --- a/test/testsuites/duma/wrong6.cc +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include -#include - -using namespace std; - -class Test { -public: - int a; - string stdstr; - - Test() { - a=2; - stdstr = "test"; - } - - void doNothing() { - cout << " hi!" << endl; - }; - -}; - -int main() { - cout << "Hello world!" << endl; - - Test ar[10]; - Test b; - cerr << "Let's index out of bounds " << endl; - ar[10].doNothing(); - cerr << "Did you notice?" << endl; - - cerr << "There should be 1 error in this run" << endl; - return 0; -} diff --git a/test/testsuites/duma/wrong7.c b/test/testsuites/duma/wrong7.c deleted file mode 100644 index 3773e75ea01..00000000000 --- a/test/testsuites/duma/wrong7.c +++ /dev/null @@ -1,13 +0,0 @@ -#include - -int main() { - int *p; - - p = (int*) malloc( sizeof(int) * 10 ); - printf("Now writing before our allocated array\n"); - p[-1] ^= 0x0F; /* bash before */ - printf("... and now after our allocated array\n"); - p[10] ^= 0x0F; /* bash after */ - printf("Did you notice?\n"); - free(p); -} diff --git a/test/testsuites/readme.txt b/test/testsuites/readme.txt deleted file mode 100644 index f912f173029..00000000000 --- a/test/testsuites/readme.txt +++ /dev/null @@ -1 +0,0 @@ -Useful test cases taken from public test suites diff --git a/test/testsuites/x-flow/buffer01.cpp b/test/testsuites/x-flow/buffer01.cpp deleted file mode 100644 index 3dc44d62eb8..00000000000 --- a/test/testsuites/x-flow/buffer01.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include - -// Simple for loop - -void f() { - char* buf = (char*) malloc(9); - int i; - for (i = 0; i < 12; i++) { - buf[i] = 's'; - } -} - -int main() { - f(); - return 0; -} diff --git a/test/testsuites/x-flow/buffer02.cpp b/test/testsuites/x-flow/buffer02.cpp deleted file mode 100644 index 0590d514257..00000000000 --- a/test/testsuites/x-flow/buffer02.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include - -// Simple while loop - -void f() { - char* buf = (char*) malloc(9); - int i = 0; - while (i < 12) { - buf[i] = 's'; - i++; - } -} - -int main() { - f(); - return 0; -} diff --git a/test/testsuites/x-flow/buffer03.cpp b/test/testsuites/x-flow/buffer03.cpp deleted file mode 100644 index ec343e4ed11..00000000000 --- a/test/testsuites/x-flow/buffer03.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include - -// Reallocation - -void f() { - char* buf = (char*) malloc(20); - buf[6] = 'x'; - buf = (char*) realloc(buf, 9); - int i = 0; - while (i < 12) { - buf[i] = 's'; - i++; - } -} - -int main() { - f(); - return 0; -} diff --git a/test/testsuites/x-flow/buffer04.cpp b/test/testsuites/x-flow/buffer04.cpp deleted file mode 100644 index 4b605c7e0e7..00000000000 --- a/test/testsuites/x-flow/buffer04.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include - -// Nested loops - -void f() { - char* buf = (char*) malloc(9); - int i, j; - for (i = 0; i < 3; i++) { - for (j = 0; j < 6; j++) { - buf[i*j] = 's'; - } - } -} - -int main() { - f(); - return 0; -} diff --git a/test/testsuites/x-flow/buffer05.cpp b/test/testsuites/x-flow/buffer05.cpp deleted file mode 100644 index 0fdfaa280b0..00000000000 --- a/test/testsuites/x-flow/buffer05.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include - -// Copy string - -void f() { - char* buf = (char*) malloc(9); - strcpy(buf, "Too big to fit"); -} - -int main() { - f(); - return 0; -} diff --git a/test/testsuites/x-flow/buffer06.cpp b/test/testsuites/x-flow/buffer06.cpp deleted file mode 100644 index 010534c0faf..00000000000 --- a/test/testsuites/x-flow/buffer06.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include - -// More complex object - -void f() { - struct Person { - const char* name; - int age; - }; - Person* people = (Person*) malloc(9 * sizeof(Person)); - for (int i = 0; i < 12; i++) { - people[i].name = "John"; - people[i].age = 23; - } -} - -int main() { - f(); - return 0; -} diff --git a/tools/dmake.cpp b/tools/dmake.cpp index 29e70e38595..1db97167029 100644 --- a/tools/dmake.cpp +++ b/tools/dmake.cpp @@ -481,7 +481,7 @@ int main(int argc, char **argv) fout << "man/cppcheck.1:\t$(MAN_SOURCE)\n\n"; fout << "\t$(XP) $(DB2MAN) $(MAN_SOURCE)\n\n"; fout << "tags:\n"; - fout << "\tctags -R --exclude=doxyoutput --exclude=test/cfg --exclude=test/synthetic cli externals gui lib test\n\n"; + fout << "\tctags -R --exclude=doxyoutput --exclude=test/cfg cli externals gui lib test\n\n"; fout << "install: cppcheck\n"; fout << "\tinstall -d ${BIN}\n"; fout << "\tinstall cppcheck ${BIN}\n";