1
0
mirror of https://github.com/mosbth/cimage.git synced 2025-08-27 09:34:30 +02:00

Compare commits

...

105 Commits
078 ... resize

Author SHA1 Message Date
Mikael Roos
daf39105fb make tests pass on travis 2017-03-03 22:59:07 +01:00
Mikael Roos
026e01b2cd made passing behat 2017-03-03 22:53:29 +01:00
Mikael Roos
0f0d954e61 prepare to run on circleci 2017-03-03 22:04:59 +01:00
Mikael Roos
58d83ba494 prepare to run on circleci 2017-03-03 21:41:35 +01:00
Mikael Roos
4f8fb82baf change how path is working in makefile 2017-03-03 21:40:46 +01:00
Mikael Roos
1bd8965535 rearranged Makefile 2017-03-03 21:01:37 +01:00
Mikael Roos
081601ebbb merged with master 2017-03-03 20:38:35 +01:00
Mikael Roos
401478c839 Correct XSS injection in check_system.php. 2016-08-31 15:26:14 +02:00
Mikael Roos
f0ab9479d6 read lena.tif, will need it in the future 2016-08-13 18:33:09 +02:00
Mikael Roos
9ff7a61ca9 remove tif version of lena 2016-08-13 17:49:36 +02:00
Mikael Roos
3170beb832 Composer suggests ext-imagick and ext-curl. 2016-08-11 17:39:45 +02:00
Mikael Roos
8001f72a1a prepare to tag v0.7.18 2016-08-09 13:27:46 +02:00
Mikael Roos
0f9e0220f1 Made &lossless a requirement to not use the original image. 2016-08-09 13:27:30 +02:00
Mikael Roos
e59ef91991 prepare to tag v0.7.17 2016-08-09 13:23:04 +02:00
Mikael Roos
2337dbe94c Made &lossless part of the generated cache filename. 2016-08-09 13:22:33 +02:00
Mikael Roos
c5de59a754 prepare to tag v0.7.16 2016-08-09 13:02:25 +02:00
Mikael Roos
7ab19d39d6 adding support for pngquant 2016-08-09 13:01:38 +02:00
Mikael Roos
9f6cba9292 changed date of release 2016-08-09 10:21:41 +02:00
Mikael Roos
21e53887b8 prepare to tag v0.7.15 2016-08-09 10:19:49 +02:00
Mikael Roos
66c5a07767 webp partly working 2016-08-09 09:32:02 +02:00
Mikael Roos
bbfd895c4c webp implemented but fail to verify 2016-08-08 16:49:29 +02:00
Mikael Roos
b5de49d601 add debug for test 2016-08-08 16:48:23 +02:00
Mikael Roos
7677fc772f remove debugging 2016-08-08 16:21:36 +02:00
Mikael Roos
3d0b25abe0 test support for webp images 2016-08-08 16:13:51 +02:00
Mikael Roos
1e5de9d225 Added the [Lenna/Lena sample
image](http://www.cs.cmu.edu/~chuck/lennapg/) as tif and created a png,
jpeg and webp version using Imagick convert `convert lena.tif
lena.{png,jpg,webp}`.
2016-08-08 14:42:12 +02:00
Mikael Roos
43cb5b79b2 prepare to tag v0.7.14 2016-08-08 12:20:01 +02:00
Mikael Roos
8e10e9ba5c Re-add removed cache directory. 2016-08-08 12:19:31 +02:00
Mikael Roos
1738680301 Make fast track cache disabled by default in the config file. 2016-08-08 12:19:04 +02:00
Mikael Roos
9b110037b4 prepare to tag v0.7.13 2016-08-08 11:35:12 +02:00
Mikael Roos
9a912e7f01 prepare to merge 2016-08-08 11:33:04 +02:00
Mikael Roos
689865a8b2 even more testing 2016-08-08 11:19:53 +02:00
Mikael Roos
493ed45311 more test 2016-08-08 10:44:27 +02:00
Mikael Roos
72c04632b8 more test 2016-08-08 10:39:11 +02:00
Mikael Roos
b1d0cb1506 prepare to test 2016-08-08 09:29:26 +02:00
Mikael Roos
c637fa23ef test with imgf 2016-07-18 16:38:57 +02:00
Mikael Roos
6cccf5497d added and fixed fast track cache script version 2016-07-18 01:00:55 +02:00
Mikael Roos
0dd562aa61 add really fast track cache script 2016-07-18 00:44:46 +02:00
Mikael Roos
9a0a9429db make debug depend on file exists 2016-07-10 22:57:58 +02:00
Mikael Roos
6118f298ff prepare to test fast track cache 2016-07-10 22:54:11 +02:00
Mikael Roos
547259cd11 intermediate 2016-07-10 18:18:17 +02:00
Mikael Roos
fbaf05d0d4 adding behat for feature testing 2016-06-03 11:44:27 +02:00
Mikael Roos
3d2c74e9df adding local dev environment 2016-06-02 10:16:18 +02:00
Mikael Roos
74428f066c passed test for resize strategies including no upscale using test/option-no-upscale 2016-06-01 13:57:48 +02:00
Mikael Roos
00ab2d7ca6 mergin parts from latest 2016-06-01 10:39:27 +02:00
Mikael Roos
32a23894d1 cleanup, removed code comments, codestyle 2016-06-01 10:11:44 +02:00
Mikael Roos
d5546b669b spelling 2016-06-01 10:06:10 +02:00
Mikael Roos
53fb8da987 tests to be written 2016-06-01 10:05:20 +02:00
Mikael Roos
226f8adbeb merged changes from v0.7.12 2016-06-01 10:04:54 +02:00
Mikael Roos
cde8bab6e7 tagging v0.7.12 2016-06-01 09:37:28 +02:00
Mikael Roos
5edbfc9b54 prepare to tag v0.7.12 2016-06-01 09:34:30 +02:00
Mikael Roos
9e9c44c935 Fixed to correctly display image when using a resize strategy without height or width. 2016-06-01 09:31:28 +02:00
Mikael Roos
9088647d3a Fixed background color for option , #144. 2016-06-01 09:30:18 +02:00
Mikael Roos
8ad324b4f5 update readme with latest version number 2016-04-18 16:09:49 +02:00
Mikael Roos
3b16b4b79d prepare to tag v0.7.11 2016-04-18 15:58:39 +02:00
Mikael Roos
4e940164f9 Add option for skip_original to config file to always skip original, #118. 2016-04-18 15:53:55 +02:00
Mikael Roos
1943d6606b update years in license 2016-04-01 11:25:36 +02:00
Mikael Roos
5eebaa66ce update to latest version 2016-04-01 10:32:44 +02:00
Mikael Roos
c5cc0314c2 prepare to tag v0.7.10 2016-04-01 10:30:25 +02:00
Mikael Roos
71816261f2 prepare to merge master 2016-04-01 10:27:33 +02:00
Mikael Roos
a62d7cb6c2 Add backup option for images src-alt, #141. 2016-04-01 10:24:24 +02:00
Mikael Roos
29d18c4b3c minor 2016-03-09 18:03:08 +01:00
Mikael Roos
d697809a69 update array short syntax 2016-03-09 12:22:03 +01:00
Mikael Roos
8667a9ec79 move alias settings to own section 2016-03-09 12:12:02 +01:00
Mikael Roos
ccbd08949f add for testcase #134 2016-01-26 17:40:10 +01:00
Mikael Roos
6467fcc748 Add require of ext-gd in composer.json, #133. 2016-01-14 16:19:00 +01:00
Mikael Roos
14d22a18e5 Merge pull request #133 from abcdmitry/patch-1
Add GD extension to the composer requiremets
2016-01-14 16:17:43 +01:00
Mikael Roos
ca6118b355 intermediate 2016-01-14 16:15:09 +01:00
Dmitry Lukashin
6d3687d838 Add PHP extensions to the composer requirements 2016-01-14 16:03:13 +03:00
Mikael Roos
cf2fc3de4f intermediate 2015-12-11 10:07:16 +01:00
Mikael Roos
ad8f6c12ee creating bundles 2015-12-07 17:40:00 +01:00
Mikael Roos
ad4930c3ae Fix strict mode only reporting 404 when failure, #127. 2015-12-07 17:39:34 +01:00
Mikael Roos
276f46fce2 merged img.php 2015-12-07 17:35:53 +01:00
Mikael Roos
0f3c1b4bde merged v0.7.9 into resize 2015-12-07 17:12:27 +01:00
Mikael Roos
f250f7dff9 recreating bundles 2015-12-07 16:02:17 +01:00
Mikael Roos
05c11ca9fc refixing new cache management for remote images 2015-12-07 16:01:47 +01:00
Mikael Roos
b069e322e9 create bundles 2015-12-07 15:36:06 +01:00
Mikael Roos
6e0c775ede clear whitespace 2015-12-07 15:35:19 +01:00
Mikael Roos
179469334a bundles to include CCache 2015-12-07 15:31:41 +01:00
Mikael Roos
0b2723feee Strict mode only reporting 404 when failure, #127. 2015-12-07 15:30:34 +01:00
Mikael Roos
c009f423a2 tested cahce with remote 2015-12-07 15:22:52 +01:00
Mikael Roos
79a7fd17d8 improved cache handling for remote, #130 2015-12-07 15:12:20 +01:00
Mikael Roos
f2153e27a8 update to use cimage version 2015-12-07 11:10:47 +01:00
Mikael Roos
3271d165ff add correct version to remote headers, fix #131 2015-12-07 10:09:26 +01:00
Mikael Roos
7e5937d7ec ignore mac files 2015-12-06 20:53:36 +01:00
Mikael Roos
3c77a63fbc ignore coverage 2015-12-06 20:52:29 +01:00
Mikael Roos
13da8b0573 add composer validate to travis tests 2015-12-06 20:51:29 +01:00
Mikael Roos
1dc04e7c53 fix invalid composer.json 2015-12-06 20:45:44 +01:00
Mikael Roos
376a40e538 adding CCache to improve cache handling of dummy and srgb 2015-12-06 19:53:34 +01:00
Mikael Roos
29743b75ec prepare to tag v0.7.8 2015-12-06 12:47:47 +01:00
Mikael Roos
902c0aaef8 generate new bundles 2015-12-06 12:40:54 +01:00
Mikael Roos
6c487c6f34 ignore build dir 2015-12-06 12:38:17 +01:00
Mikael Roos
a1d811a318 Merge branch '078' 2015-12-06 12:37:11 +01:00
Mikael Roos
a205d65ad5 moving to new travis environment 2015-12-06 12:27:54 +01:00
Mikael Roos
52ce33d928 stretch not applying no upscale 2015-12-06 11:55:09 +01:00
Mikael Roos
5a09c38f5d resize-strategies together with no upscale 2015-12-06 01:24:30 +01:00
Mikael Roos
679714422d fix phpunit for scrutinizer 2015-12-05 17:19:49 +01:00
Mikael Roos
ec72246e9c allow travis failure on hhvm and nightly 2015-12-05 15:04:19 +01:00
Mikael Roos
389c3c48ee install phpcs into build dir on travis 2015-12-05 14:59:17 +01:00
Mikael Roos
fcedac6e62 scrutinizer to make build-dir 2015-12-05 14:58:01 +01:00
Mikael Roos
3daa8549ab update travis to use build directory 2015-12-05 14:53:51 +01:00
Mikael Roos
6a93f843be major rearrange to prepare to move to PHP 5.4 2015-12-05 14:50:49 +01:00
Mikael Roos
f9b149eb5d merged changes from master into resize 2015-12-05 13:58:57 +01:00
Mikael Roos
e41e23c007 adding new tests for resize 2015-11-25 09:58:37 +01:00
Mikael Roos
5673ce4f55 intermediate 2015-11-25 09:58:09 +01:00
Mikael Roos
9bf94e9cf6 calculate target dimensions 2015-10-27 00:21:36 +01:00
88 changed files with 6784 additions and 1189 deletions

13
.gitignore vendored
View File

@@ -1,10 +1,17 @@
# Cache files
cache/*
# Test
# Test & build
build/
coverage/
coverage.clover
# Composer
composer.lock
vendor
#vendor/
# Build and test
build/
/.bin
# Mac OS
.DS_Store

View File

@@ -2,12 +2,12 @@
<ruleset name="PHPCS rule set">
<description>Custom rule set.</description>
<file>.</file>
<file>src</file>
<file>test</file>
<file>autoload.php</file>
<exclude-pattern>docs/*</exclude-pattern>
<exclude-pattern>coverage/*</exclude-pattern>
<exclude-pattern>build/*</exclude-pattern>
<exclude-pattern>test/config.php</exclude-pattern>
<exclude-pattern>webroot/imgs.php</exclude-pattern>
<exclude-pattern>webroot/imgp.php</exclude-pattern>
<exclude-pattern>webroot/imgd.php</exclude-pattern>

26
.phpunit.xml Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpunit
bootstrap="test/config.php">
<testsuites>
<testsuite name="all">
<directory>test</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src/</directory>
<exclude>
<directory suffix=".php">test</directory>
<directory suffix=".php">webroot</directory>
</exclude>
</whitelist>
</filter>
<logging>
<log type="coverage-html" target="build/coverage" charset="UTF-8" highlight="true" lowUpperBound="35" highLowerBound="70" />
<log type="coverage-clover" target="build/coverage.clover" />
</logging>
</phpunit>

View File

@@ -10,37 +10,45 @@ filter:
- webroot/imgs.php
- webroot/test/
checks:
php:
code_rating: true
duplication: true
#checks:
# php:
# code_rating: true
# duplication: true
tools:
#tools:
# Copy/Paste Detector
php_cpd: true
#php_cpd: true
# Metrics
php_pdepend: true
#php_pdepend: true
# Some Metrics + Bug Detection/Auto-Fixes
php_analyzer: true
#php_analyzer: true
php_code_sniffer:
config:
standard: "PSR2"
#php_code_sniffer:
# config:
# standard: "PSR2"
php_sim:
min_mass: 16 # Defaults to 16
#php_sim:
# min_mass: 16 # Defaults to 16
php_mess_detector:
#php_mess_detector:
#config:
# ruleset: ../your-phpmd-ruleset/ruleset.xml
build:
dependencies:
before:
-
command: 'mkdir build'
tests:
override:
-
command: 'phpunit'
command: 'phpunit --configuration .phpunit.xml'
coverage:
file: 'coverage.clover'
file: 'build/coverage.clover'
format: 'php-clover'

View File

@@ -4,9 +4,10 @@ php:
- 5.4
- 5.5
- 5.6
- "7.0"
- "7.1"
- hhvm
- nightly
- "7.0"
@@ -35,49 +36,13 @@ matrix:
before_script:
# Create a build directory for output
# Store all files in your own bin
#- install --directory build/bin
#- export PATH=$PATH:$PWD/build/bin/
# Install validation tools
#- npm install -g htmlhint csslint jshint jscs jsonlint js-yaml html-minifier@0.8.0 clean-css uglify-js
# Install phpcs
#- curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar
#- install --mode 0755 phpcs.phar $PWD/build/bin/phpcs
# Install phpmd
#- wget -c http://static.phpmd.org/php/latest/phpmd.phar
#- install --mode 0755 phpmd.phar $PWD/build/bin/phpmd
- make install
- make check
script:
# Check versions of validation tools
#- node --version
#- npm --version
#- htmlhint --version
#- csslint --version
#- jscs --version
#- jshint --version
#- phpcs --version
#- phpmd --version
#- jsonlint --version
#- js-yaml --version
#- shellcheck --version
#- html-minifier --version
#- cleancss --version
#- uglifyjs --version
# Run validation & publish
#- make phpunit
- phpunit
#- make phpcs
- make test

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2012 - 2014 Mikael Roos, me@mikaelroos.se
Copyright (c) 2012 - 2016 Mikael Roos, https://mikaelroos.se, mos@dbwebb.se
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
THE SOFTWARE.

227
Makefile Normal file
View File

@@ -0,0 +1,227 @@
#
#
#
# Detect OS
OS = $(shell uname -s)
# Defaults
ECHO = echo
# Make adjustments based on OS
# http://stackoverflow.com/questions/3466166/how-to-check-if-running-in-cygwin-mac-or-linux/27776822#27776822
ifneq (, $(findstring CYGWIN, $(OS)))
ECHO = /bin/echo -e
endif
# Colors and helptext
NO_COLOR = \033[0m
ACTION = \033[32;01m
OK_COLOR = \033[32;01m
ERROR_COLOR = \033[31;01m
WARN_COLOR = \033[33;01m
# Which makefile am I in?
WHERE-AM-I = $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
THIS_MAKEFILE := $(call WHERE-AM-I)
# Echo some nice helptext based on the target comment
HELPTEXT = $(ECHO) "$(ACTION)--->" `egrep "^\# target: $(1) " $(THIS_MAKEFILE) | sed "s/\# target: $(1)[ ]*-[ ]* / /g"` "$(NO_COLOR)"
# Add local bin path for test tools
#PATH := "./.bin:./vendor/bin:./node_modules/.bin:$(PATH)"
#SHELL := env PATH=$(PATH) $(SHELL)
PHPUNIT := .bin/phpunit
PHPLOC := .bin/phploc
PHPCS := .bin/phpcs
PHPCBF := .bin/phpcbf
PHPMD := .bin/phpmd
PHPDOC := .bin/phpdoc
BEHAT := .bin/behat
# target: help - Displays help.
.PHONY: help
help:
@$(call HELPTEXT,$@)
@$(ECHO) "Usage:"
@$(ECHO) " make [target] ..."
@$(ECHO) "target:"
@egrep "^# target:" $(THIS_MAKEFILE) | sed 's/# target: / /g'
# target: prepare - Prepare for tests and build
.PHONY: prepare
prepare:
@$(call HELPTEXT,$@)
[ -d .bin ] || mkdir .bin
[ -d build ] || mkdir build
rm -rf build/*
# target: clean - Removes generated files and directories.
.PHONY: clean
clean:
@$(call HELPTEXT,$@)
rm -rf build
# target: clean-all - Removes generated files and directories.
.PHONY: clean-all
clean-all:
@$(call HELPTEXT,$@)
rm -rf .bin build vendor composer.lock
# target: check - Check version of installed tools.
.PHONY: check
check: check-tools-php
@$(call HELPTEXT,$@)
# target: test - Run all tests.
.PHONY: test
test: phpunit phpcs phpmd phploc behat
@$(call HELPTEXT,$@)
composer validate
# target: doc - Generate documentation.
.PHONY: doc
doc: phpdoc
@$(call HELPTEXT,$@)
# target: build - Do all build
.PHONY: build
build: test doc #less-compile less-minify js-minify
@$(call HELPTEXT,$@)
# target: install - Install all tools
.PHONY: install
install: prepare install-tools-php
@$(call HELPTEXT,$@)
# target: update - Update the codebase and tools.
.PHONY: update
update:
@$(call HELPTEXT,$@)
git pull
composer update
# target: tag-prepare - Prepare to tag new version.
.PHONY: tag-prepare
tag-prepare:
@$(call HELPTEXT,$@)
# ------------------------------------------------------------------------
#
# PHP
#
# target: install-tools-php - Install PHP development tools.
.PHONY: install-tools-php
install-tools-php:
@$(call HELPTEXT,$@)
curl -Lso $(PHPDOC) https://www.phpdoc.org/phpDocumentor.phar && chmod 755 $(PHPDOC)
curl -Lso $(PHPCS) https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar && chmod 755 $(PHPCS)
curl -Lso $(PHPCBF) https://squizlabs.github.io/PHP_CodeSniffer/phpcbf.phar && chmod 755 $(PHPCBF)
curl -Lso $(PHPMD) http://static.phpmd.org/php/latest/phpmd.phar && chmod 755 $(PHPMD)
curl -Lso $(PHPUNIT) https://phar.phpunit.de/phpunit-5.7.9.phar && chmod 755 $(PHPUNIT)
curl -Lso $(PHPLOC) https://phar.phpunit.de/phploc.phar && chmod 755 $(PHPLOC)
curl -Lso $(BEHAT) https://github.com/Behat/Behat/releases/download/v3.3.0/behat.phar && chmod 755 $(BEHAT)
composer install
# target: check-tools-php - Check versions of PHP tools.
.PHONY: check-tools-php
check-tools-php:
@$(call HELPTEXT,$@)
which $(PHPUNIT) && $(PHPUNIT) --version
which $(PHPLOC) && $(PHPLOC) --version
which $(PHPCS) && $(PHPCS) --version && echo
which $(PHPMD) && $(PHPMD) --version && echo
which $(PHPCBF) && $(PHPCBF) --version && echo
which $(PHPDOC) && $(PHPDOC) --version && echo
which $(BEHAT) && $(BEHAT) --version && echo
# target: phpunit - Run unit tests for PHP.
.PHONY: phpunit
phpunit: prepare
@$(call HELPTEXT,$@)
$(PHPUNIT) --configuration .phpunit.xml
# target: phpcs - Codestyle for PHP.
.PHONY: phpcs
phpcs: prepare
@$(call HELPTEXT,$@)
$(PHPCS) --standard=.phpcs.xml | tee build/phpcs
# target: phpcbf - Fix codestyle for PHP.
.PHONY: phpcbf
phpcbf:
@$(call HELPTEXT,$@)
$(PHPCBF) --standard=.phpcs.xml
# target: phpmd - Mess detector for PHP.
.PHONY: phpmd
phpmd: prepare
@$(call HELPTEXT,$@)
- $(PHPMD) . text .phpmd.xml | tee build/phpmd
# target: phploc - Code statistics for PHP.
.PHONY: phploc
phploc: prepare
@$(call HELPTEXT,$@)
$(PHPLOC) src > build/phploc
# target: phpdoc - Create documentation for PHP.
.PHONY: phpdoc
phpdoc:
@$(call HELPTEXT,$@)
$(PHPDOC) --config=.phpdoc.xml
# target: behat - Run behat for feature tests.
.PHONY: behat
behat:
@$(call HELPTEXT,$@)
$(BEHAT)

View File

@@ -2,13 +2,19 @@ Image conversion on the fly using PHP
=====================================
[![Join the chat at https://gitter.im/mosbth/cimage](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mosbth/cimage?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://travis-ci.org/mosbth/cimage.svg?branch=master)](https://travis-ci.org/mosbth/cimage)
[![Build Status](https://scrutinizer-ci.com/g/mosbth/cimage/badges/build.png?b=master)](https://scrutinizer-ci.com/g/mosbth/cimage/build-status/master)
[![Build Status](https://travis-ci.org/mosbth/cimage.svg?branch=resize)](https://travis-ci.org/mosbth/cimage)
[![CircleCI](https://circleci.com/gh/mosbth/cimage.svg?style=svg)](https://circleci.com/gh/mosbth/cimage)
[![Build Status](https://scrutinizer-ci.com/g/mosbth/cimage/badges/build.png?b=resize)](https://scrutinizer-ci.com/g/mosbth/cimage/build-status/resize)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/mosbth/cimage/badges/quality-score.png?b=resize)](https://scrutinizer-ci.com/g/mosbth/cimage/?branch=resize)
[![Code Coverage](https://scrutinizer-ci.com/g/mosbth/cimage/badges/coverage.png?b=resize)](https://scrutinizer-ci.com/g/mosbth/cimage/?branch=resize)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/ebc10d27-449d-42ca-b380-ba5ecb7813ba/mini.png)](https://insight.sensiolabs.com/projects/ebc10d27-449d-42ca-b380-ba5ecb7813ba)
About
-------------------------------------
<img src="http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim07.png&w=200&c=140,140,520,340&sharpen"/>
<img src="https://cimage.se/cimage/imgd.php?src=example/kodim07.png&w=200&c=140,140,520,340&sharpen"/>
`CImage` is a PHP class enabling resizing of images through scaling, cropping and filtering effects -- using PHP GD. The script `img.php` uses `CImage` to enable server-side image processing utilizing caching and optimization of the processed images.
@@ -22,7 +28,10 @@ Documentation
--------------------------------------
Read full documentation at:
http://dbwebb.se/opensource/cimage
<strike>http://dbwebb.se/opensource/cimage</strike>
New website is being setup at [cimage.se](https://cimage.se), to improve documentation (work is ongoing).
@@ -46,14 +55,14 @@ There are several ways of installing. You either install the whole project which
The [sourcode is available on GitHub](https://github.com/mosbth/cimage). Clone, fork or [download as zip](https://github.com/mosbth/cimage/archive/master.zip).
**Latest stable version is v0.7.7 released 2015-10-21.**
**Latest stable version is v0.7.18 released 2016-08-09.**
I prefer cloning like this. Do switch to the latest stable version.
```bash
git clone git://github.com/mosbth/cimage.git
cd cimage
git checkout v0.7.7
git checkout v0.7.18
```
Make the cache-directory writable by the webserver.
@@ -76,7 +85,7 @@ There are some all-included bundles of `img.php` that can be downloaded and used
Dowload the version of your choice like this.
```bash
wget https://raw.githubusercontent.com/mosbth/cimage/v0.7.7/webroot/imgp.php
wget https://raw.githubusercontent.com/mosbth/cimage/v0.7.18/webroot/imgp.php
```
Open up the file in your editor and edit the array `$config`. Ensure that the paths to the image directory and the cache directory matches your environment, or create an own config-file for the script.
@@ -98,7 +107,7 @@ Lets take some use cases to let you know when and how `img.php` might be useful.
### Make a thumbnail
<img src="http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim04.png&w=80&h=80&cf">
<img src="https://cimage.se/cimage/imgd.php?src=example/kodim04.png&w=80&h=80&cf">
Lets say you have a larger image and you want to make a smaller thumbnail of it with a size of 80x80 pixels. You simply take the image and add constraints on `width`, `height` and you use the resize strategy `crop-to-fit` to crops out the parts of the image that does not fit.
@@ -112,7 +121,7 @@ To produce such a thumbnail, create a link like this:
Perhaps you got an image from a friend. The image was taken with the iPhone and thus rotated.
<img src="http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=issue36/me-270.jpg&w=250">
<img src="https://cimage.se/cimage/imgd.php?src=example/issue36/me-270.jpg&w=250">
The original image is looking like this one, scaled down to a width of 250 pixels.
@@ -125,7 +134,7 @@ Lets call this *the URL-Photoshopper*. This is how the magic looks like.
> `img.php?src=issue36/me-270.jpg&w=100&h=100&cf&aro`
> `&rb=-25&a=8,30,30,38&f=grayscale&convolve=sharpen-alt`
<img src="http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=issue36/me-270.jpg&w=100&h=100&cf&aro&rb=-25&a=8,30,30,38&f=grayscale&convolve=sharpen-alt">
<img src="https://cimage.se/cimage/imgd.php?src=example/issue36/me-270.jpg&w=100&h=100&cf&aro&rb=-25&a=8,30,30,38&f=grayscale&convolve=sharpen-alt">
For myself, I use `img.php` to put up all images on my website, it gives me the power of affecting the resulting images - without opening up a photo-editing application.
@@ -144,7 +153,7 @@ Try it out by pointing your browser to the test file `webroot/test/test.php`. It
###Process your first image
<img src="http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim04.png&amp;w=w2&amp;a=40,0,50,0" alt=''>
<img src="https://cimage.se/cimage/imgd.php?src=example/kodim04.png&amp;w=w2&amp;a=40,0,50,0" alt=''>
Try it yourself by opening up an image in your browser. Start with
@@ -198,7 +207,7 @@ Open an image through `img.php` by using its `src` attribute.
It looks like this.
<img src="http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim13.png&w=w1&save-as=jpg">
<img src="https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=w1&save-as=jpg">
All images are stored in a directory structure and you access them as:
@@ -212,7 +221,7 @@ Create a thumbnail of the image by applying constraints on width and height, or
| `&width=150` | `&height=150` | `&w=150&h=150` |
|---------------------|---------------------|---------------------|
| <img src=http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim13.png&w=150 alt=''> | <img src=http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim13.png&h=150 alt=''> | <img src=http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim13.png&w=150&h=150 alt=''> |
| <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=150 alt=''> | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&h=150 alt=''> | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=150&h=150 alt=''> |
By setting `width`, `height` or both, the image gets resized to be *not larger* than the defined dimensions *and* keeping its original aspect ratio.
@@ -227,10 +236,10 @@ Creating a thumbnail with a certain dimension of width and height, usually invol
| What | The image |
|---------------------|---------------------|
| **Original.** The original image resized with a max width and max height.<br>`?w=300&h=150` | <img src=http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim13.png&w=300&h=150 alt=''> |
| **Stretch.** Stretch the image so that the resulting image has the defined width and height.<br>`?w=300&h=150&stretch` | <img src=http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim13.png&w=300&h=150&stretch alt=''> |
| **Crop to fit.** Keep the aspect ratio and crop out the parts of the image that does not fit.<br>`?w=300&h=150&crop-to-fit` | <img src=http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim13.png&w=300&h=150&crop-to-fit alt=''> |
| **Fill to fit.** Keep the aspect ratio and fill then blank space with a background color.<br>`?w=300&h=150&fill-to-fit=006600` | <img src=http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim13.png&w=300&h=150&fill-to-fit=006600 alt=''> |
| **Original.** The original image resized with a max width and max height.<br>`?w=300&h=150` | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=300&h=150 alt=''> |
| **Stretch.** Stretch the image so that the resulting image has the defined width and height.<br>`?w=300&h=150&stretch` | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=300&h=150&stretch alt=''> |
| **Crop to fit.** Keep the aspect ratio and crop out the parts of the image that does not fit.<br>`?w=300&h=150&crop-to-fit` | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=300&h=150&crop-to-fit alt=''> |
| **Fill to fit.** Keep the aspect ratio and fill then blank space with a background color.<br>`?w=300&h=150&fill-to-fit=006600` | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=300&h=150&fill-to-fit=006600 alt=''> |
Learn to crop your images, creative cropping can make wonderful images from appearingly useless originals.
@@ -247,7 +256,7 @@ Fill to fit is useful when you have some image that must fit in a certain dimens
> `img.php?src=kodim13.png&w=600&aspect-ratio=4`
> `&crop-to-fit&sharpen&save-as=jpg&q=30`
<img src=http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim13.png&w=600&aspect-ratio=4&crop-to-fit&sharpen&save-as=jpg&q=30 alt=''>
<img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=600&aspect-ratio=4&crop-to-fit&sharpen&save-as=jpg&q=30 alt=''>
Here is a list of all parameters that you can use together with `img.php`, grouped by its basic intent of usage.
@@ -430,7 +439,7 @@ For example, the following image is created like this:
> `&w=300&save-as=jpg`
<img src="http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=kodim24.png&w=300&save-as=jpg" alt=''>
<img src="https://cimage.se/cimage/imgd.php?src=example/kodim24.png&w=300&save-as=jpg" alt=''>
Its JSON-representation is retrieved like this:

View File

@@ -1,8 +1,104 @@
Revision history
=====================================
[![Build Status](https://travis-ci.org/mosbth/cimage.svg?branch=master)](https://travis-ci.org/mosbth/cimage)
[![Build Status](https://scrutinizer-ci.com/g/mosbth/cimage/badges/build.png?b=master)](https://scrutinizer-ci.com/g/mosbth/cimage/build-status/master)
v0.8.* (2015-12-05) (branch resize)
-------------------------------------
* Improving build phase using travis and scrutinizer.
* Code validating with phpunit and phpcs.
* Moved classes to src/, adding namespace and (only) support PSR-4.
* Require PHP 5.4.
v0.7.9* (2015-12-07)
-------------------------------------
* Fix strict mode only reporting 404 when failure, #127.
v0.7.19* (2016-08-31)
-------------------------------------
* Correct XSS injection in `check_system.php`.
* Composer suggests ext-imagick and ext-curl.
v0.7.18 (2016-08-09)
-------------------------------------
* Made `&lossless` a requirement to not use the original image.
v0.7.17 (2016-08-09)
-------------------------------------
* Made `&lossless` part of the generated cache filename.
v0.7.16 (2016-08-09)
-------------------------------------
* Fix default mode to be production.
* Added pngquant as extra postprocessing utility for PNG-images, #154.
* Bug `&status` wrong variable name for fast track cache.
v0.7.15 (2016-08-09)
-------------------------------------
* Added the [Lenna/Lena sample image](http://www.cs.cmu.edu/~chuck/lennapg/) as tif and created a png, jpeg and webp version using Imagick convert `convert lena.tif lena.{png,jpg,webp}`, #152.
* Limited and basic support for WEBP format, se #132.
v0.7.14 (2016-08-08)
-------------------------------------
* Re-add removed cache directory.
* Make fast track cache disabled by default in the config file.
v0.7.13 (2016-08-08)
-------------------------------------
* Moved functions from img.php to `functions.php`.
* Added function `trace()` to measure speed and memory consumption, only for development.
* Added fast cache #149.
* Added `imgf.php` as shortcut to check for fast cache, before loading `img.php` as usual, adding `imgf_config.php` as symlink to `img_config.php`.
* Created `defines.php` and moved definition av version there.
* Fixed images in README, #148.
* Initiated dependency injection to `CImage`, class names can be set in config file and will be injected to `CImage` from `img.php`. Not implemented for all classes. #151.
* Enabled debug mode to make it easier to trace what actually happens while processing the image, #150.
v0.7.12 (2016-06-01)
-------------------------------------
* Fixed to correctly display image when using a resize strategy without height or width.
* Fixed background color for option `no-upscale`, #144.
v0.7.11 (2016-04-18)
-------------------------------------
* Add option for `skip_original` to config file to always skip original, #118.
v0.7.10 (2016-04-01)
-------------------------------------
* Add backup option for images `src-alt`, #141.
* Add require of ext-gd in composer.json, #133.
* Fix strict mode only reporting 404 when failure, #127.
v0.7.9 (2015-12-07)
-------------------------------------
* Strict mode only reporting 404 when failure, #127.
* Added correct CImage version to remote agent string, #131.
* Adding CCache to improve cache handling of caching for dummy, remote and srgb. #130.
v0.7.8 (2015-12-06)

View File

@@ -34,11 +34,15 @@ fi
$ECHO "Creating '$TARGET_D', '$TARGET_P' and '$TARGET_S' by combining the following files:"
$ECHO "\n"
$ECHO "\n webroot/img_header.php"
$ECHO "\n defines.php"
$ECHO "\n functions.php"
$ECHO "\n CHttpGet.php"
$ECHO "\n CRemoteImage.php"
$ECHO "\n CWhitelist.php"
$ECHO "\n CAsciiArt.php"
$ECHO "\n CImage.php"
$ECHO "\n CCache.php"
$ECHO "\n CFastTrackCache.php"
$ECHO "\n webroot/img.php"
$ECHO "\n"
$ECHO "\n'$TARGET_D' is for development mode."
@@ -59,6 +63,12 @@ cat webroot/img_header.php | sed "s|//'mode' => 'production',|'mode'
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
tail -n +2 defines.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
tail -n +2 functions.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
tail -n +2 CHttpGet.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
@@ -74,6 +84,12 @@ $ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
tail -n +2 CImage.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
tail -n +2 CCache.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
tail -n +2 CFastTrackCache.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
tail -n +2 webroot/img.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

4
cache/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

0
cache/README.md vendored
View File

View File

13
circle.yml Normal file
View File

@@ -0,0 +1,13 @@
machine:
php:
#version: 5.6.18
test:
pre:
- make install
- make check
override:
- make test

View File

@@ -1,32 +1,39 @@
{
"name": "mos/cimage",
"name": "mos/cimage",
"type": "library",
"description": "Process, scale, resize, crop and filter images.",
"keywords": ["image", "imageprocessing", "gd"],
"homepage": "http://dbwebb.se/opensource/cimage",
"description": "Serverside image processing with PHP GD. Process, scale, resize, crop and filter images.",
"keywords": ["image", "imageprocessing", "gd", "crop", "resize"],
"homepage": "https://cimage.se/",
"license": "MIT",
"authors": [
{
"name": "Mikael Roos",
"email": "me@mikaelroos.se",
"email": "mos@dbwebb.se",
"homepage": "http://mikaelroos.se",
"role": "Developer"
}
],
"support": {
"source": "https://github.com/mosbth/cimage",
"issues": "https://github.com/mosbth/cimage/issues",
"docs": "http://dbwebb.se/opensource/cimage"
"docs": "https://cimage.se/doc"
},
"require": {
"php": ">=5.3"
"php": ">=5.4",
"ext-gd": "*"
},
"suggest": {
"ext-exif": "*",
"ext-curl": "*",
"ext-imagick": "*"
},
"autoload": {
"classmap": [
"CImage.php",
"CHttpGet.php",
"CRemoteImage.php",
"CWhitelist.php",
"CAsciiArt.php"
"psr-4": {
"Mos\\": "src/"
},
"files": [
"src/defines.php",
"src/functions.php"
]
}
}

20
composer.lock generated Normal file
View File

@@ -0,0 +1,20 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "336411b967cf65e460236df4df7e340a",
"packages": [],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.4",
"ext-gd": "*"
},
"platform-dev": []
}

View File

@@ -0,0 +1,208 @@
<?php
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
require __DIR__ . "/assert.php";
/**
* Defines application features from the specific context.
*/
class FeatureContext implements Context, SnippetAcceptingContext
{
private $url = null;
private $headers = [];
private $imageString = null;
private $image = null;
private $imageJSON = null;
/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/
public function __construct()
{
}
/**
* @Given Set mode :arg1
*/
public function setMode($arg1 = null)
{
$this->url = "http://localhost/git/cimage/webroot/";
switch ($arg1) {
case "development":
$this->url .= "imgd.php";
break;
case "production":
$this->url .= "imgp.php";
break;
case "strict":
$this->url .= "imgs.php";
break;
default:
$this->url .= "img.php";
}
}
/**
* @Given Set src :arg1
*/
public function setSrc($arg1)
{
if (is_null($this->url)) {
$this->setMode();
}
$this->url .= "?src=$arg1";
}
/**
* @When Get image
*/
public function getImage()
{
//echo $this->url;
$res = file_get_contents($this->url);
assertNotEquals(false, $res);
$this->imageString = $res;
$this->headers = $http_response_header;
if (is_null($this->imageJSON)) {
$this->getImageAsJson();
}
}
/**
* @When Get image as JSON
*/
public function getImageAsJson()
{
$res = file_get_contents($this->url . "&json");
assertNotEquals(false, $res);
$res = json_decode($res, true);
assertNotEquals(null, $res);
$this->imageJSON = $res;
}
/**
* @When Get headers for image
*/
public function getHeadersForImage()
{
//echo $this->url;
$res = get_headers($this->url);
assertNotEquals(false, $res);
$this->headers = $http_response_header;
}
/**
* @Then Returns status code :arg1
*/
public function returnsStatusCode($arg1)
{
assertNotEquals(
false,
strpos($this->headers[0], $arg1)
);
}
/**
*
*/
private function compareImageJsonToHeaders()
{
$contentLength = "Content-Length: " . $this->imageJSON["size"];
assertContains(
$contentLength,
$this->headers
);
$contentType = "Content-Type: " . $this->imageJSON["mimeType"];
assertContains(
$contentType,
$this->headers
);
$lastModified = "Last-Modified: " . $this->imageJSON["cacheGmdate"] . " GMT";
assertContains(
$lastModified,
$this->headers
);
}
/**
*
*/
private function compareImageJsonToSavedJson($file)
{
$res = file_get_contents("$file.json");
assertNotEquals(false, $res);
$res = json_decode($res, true);
assertNotEquals(null, $res);
$keys = [
"mimeType",
"width",
"height",
"size",
"colors",
"pngType",
];
foreach ($keys as $key) {
if (array_key_exists($key, $res)
&& array_key_exists($key, $this->imageJSON)
) {
assertEquals(
$res[$key],
$this->imageJSON[$key]
);
}
}
}
/**
* @Then Compares to image :arg1
*/
public function comparesToImage($arg1)
{
$base = __DIR__ . "/../img";
$res = file_get_contents("$base/$arg1");
assertNotEquals(false, $res);
assertEquals($this->imageString, $res);
$this->compareImageJsonToHeaders();
$this->compareImageJsonToSavedJson("$base/$arg1");
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* Assert functions.
*/
function assertNotEquals($expected, $actual)
{
if (!($expected !== $actual)) {
throw new Exception("Failed asserting that '$expected' is not equal to '$actual'.");
}
};
function assertEquals($expected, $actual)
{
if (!($expected === $actual)) {
throw new Exception("Failed asserting that '$expected' is equal to '$actual'.");
}
};
/**
* Check that $needle is an element of $haystack.
*/
function assertContains($needle, $haystack)
{
if (!in_array($needle, $haystack)) {
throw new Exception("Failed asserting that '$needle' is not in haystack.");
}
}

8
features/dummy.feature Normal file
View File

@@ -0,0 +1,8 @@
Feature: dummy
Display an dummy image without using an existing image on file.
Scenario: Set source to be dummy
Given Set src "dummy"
When Get image
Then Returns status code "200"
And Compares to image "dummy"

BIN
features/img/dummy Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

19
features/img/dummy.json Normal file
View File

@@ -0,0 +1,19 @@
{
"src": "dummy_100_100_q60_co-1",
"srcGmdate": "Fri, 03 Jun 2016 07:38:56",
"cache": "_._dummy_100_100_q60_co-1_q60_co-1",
"cacheGmdate": "Fri, 03 Jun 2016 07:38:56",
"filename": "_._dummy_100_100_q60_co-1_q60_co-1",
"mimeType": "image/png",
"width": 100,
"height": 100,
"aspectRatio": 1,
"size": 334,
"colors": 1,
"includedFiles": 6,
"memoryPeek": "0.341 MB",
"memoryCurrent": "0.316 MB",
"memoryLimit": "128M",
"loadTime": "0.01s",
"pngType": "PNG is type 6, RGB with alpha channel (PNG 32-bit)"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

View File

@@ -0,0 +1,19 @@
{
"src": "test_100x100.png",
"srcGmdate": "Fri, 03 Jun 2016 08:12:39",
"cache": "_._test_100x100.png_q60_co-1",
"cacheGmdate": "Fri, 03 Jun 2016 08:19:37",
"filename": "_._test_100x100.png_q60_co-1",
"mimeType": "image/png",
"width": 100,
"height": 100,
"aspectRatio": 1,
"size": 392,
"colors": 6,
"includedFiles": 6,
"memoryPeek": "0.339 MB",
"memoryCurrent": "0.316 MB",
"memoryLimit": "128M",
"loadTime": "0.012s",
"pngType": "PNG is type 6, RGB with alpha channel (PNG 32-bit)"
}

13
features/src.feature Normal file
View File

@@ -0,0 +1,13 @@
Feature: src
Display an image by selecting its source.
Scenario: Source is not a valid image name
Given Set src "NO_IMAGE"
When Get headers for image
Then Returns status code "404"
Scenario: Get only source image
Given Set src "test_100x100.png"
When Get image
Then Returns status code "200"
And Compares to image "test_100x100.png"

View File

@@ -3,9 +3,10 @@
* Autoloader for CImage and related class files.
*
*/
//include __DIR__ . "/../CHttpGet.php";
//include __DIR__ . "/../CRemoteImage.php";
//include __DIR__ . "/../CImage.php";
require_once __DIR__ . "/defines.php";
require_once __DIR__ . "/functions.php";
/**
* Autoloader for classes.
@@ -15,8 +16,7 @@
* @return void
*/
spl_autoload_register(function ($class) {
//$path = CIMAGE_SOURCE_PATH . "/{$class}.php";
$path = __DIR__ . "/{$class}.php";
$path = __DIR__ . "/src/CImage/{$class}.php";
if (is_file($path)) {
require($path);
}

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpunit
bootstrap="test/config.php">
<testsuites>
<testsuite name="all">
<directory>test</directory>
</testsuite>
</testsuites>
<logging>
<log type="coverage-html" target="coverage" charset="UTF-8" highlight="true" lowUpperBound="35" highLowerBound="70" />
<log type="coverage-clover" target="coverage.clover" />
</logging>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<file>*.php</file>
</whitelist>
</filter>
</phpunit>

View File

@@ -1,4 +1,7 @@
<?php
namespace Mos\CImage;
/**
* Create an ASCII version of an image.
*
@@ -175,7 +178,7 @@ class CAsciiArt
*/
public function getLuminance($red, $green, $blue)
{
switch($this->luminanceStrategy) {
switch ($this->luminanceStrategy) {
case 1:
$luminance = ($red * 0.2126 + $green * 0.7152 + $blue * 0.0722) / 255;
break;

110
src/CImage/CCache.php Normal file
View File

@@ -0,0 +1,110 @@
<?php
namespace Mos\CImage;
/**
* Deal with the cache directory and cached items.
*
*/
class CCache
{
/**
* Path to the cache directory.
*/
private $path;
/**
* Set the path to the cache dir which must exist.
*
* @param string path to the cache dir.
*
* @throws Exception when $path is not a directory.
*
* @return $this
*/
public function setDir($path)
{
if (!is_dir($path)) {
throw new Exception("Cachedir is not a directory.");
}
$this->path = $path;
return $this;
}
/**
* Get the path to the cache subdir and try to create it if its not there.
*
* @param string $subdir name of subdir
* @param array $create default is to try to create the subdir
*
* @return string | boolean as real path to the subdir or
* false if it does not exists
*/
public function getPathToSubdir($subdir, $create = true)
{
$path = realpath($this->path . "/" . $subdir);
if (is_dir($path)) {
return $path;
}
if ($create && is_writable($this->path)) {
$path = $this->path . "/" . $subdir;
if (mkdir($path)) {
return realpath($path);
}
}
return false;
}
/**
* Get status of the cache subdir.
*
* @param string $subdir name of subdir
*
* @return string with status
*/
public function getStatusOfSubdir($subdir)
{
$path = realpath($this->path . "/" . $subdir);
$exists = is_dir($path);
$res = $exists ? "exists" : "does not exist";
if ($exists) {
$res .= is_writable($path) ? ", writable" : ", not writable";
}
return $res;
}
/**
* Remove the cache subdir.
*
* @param string $subdir name of subdir
*
* @return null | boolean true if success else false, null if no operation
*/
public function removeSubdir($subdir)
{
$path = realpath($this->path . "/" . $subdir);
if (is_dir($path)) {
return rmdir($path);
}
return null;
}
}

View File

@@ -0,0 +1,241 @@
<?php
namespace Mos\CImage;
/**
* Enable a fast track cache with a json representation of the image delivery.
*
*/
class CFastTrackCache
{
/**
* Cache is disabled to start with.
*/
private $enabled = false;
/**
* Path to the cache directory.
*/
private $path;
/**
* Filename of current cache item.
*/
private $filename;
/**
* Container with items to store as cached item.
*/
private $container;
/**
* Enable or disable cache.
*
* @param boolean $enable set to true to enable, false to disable
*
* @return $this
*/
public function enable($enabled)
{
$this->enabled = $enabled;
return $this;
}
/**
* Set the path to the cache dir which must exist.
*
* @param string $path to the cache dir.
*
* @throws Exception when $path is not a directory.
*
* @return $this
*/
public function setCacheDir($path)
{
if (!is_dir($path)) {
throw new Exception("Cachedir is not a directory.");
}
$this->path = rtrim($path, "/");
return $this;
}
/**
* Set the filename to store in cache, use the querystring to create that
* filename.
*
* @param array $clear items to clear in $_GET when creating the filename.
*
* @return string as filename created.
*/
public function setFilename($clear)
{
$query = $_GET;
// Remove parts from querystring that should not be part of filename
foreach ($clear as $value) {
unset($query[$value]);
}
arsort($query);
$queryAsString = http_build_query($query);
$this->filename = md5($queryAsString);
if (CIMAGE_DEBUG) {
$this->container["query-string"] = $queryAsString;
}
return $this->filename;
}
/**
* Add header items.
*
* @param string $header add this as header.
*
* @return $this
*/
public function addHeader($header)
{
$this->container["header"][] = $header;
return $this;
}
/**
* Add header items on output, these are not output when 304.
*
* @param string $header add this as header.
*
* @return $this
*/
public function addHeaderOnOutput($header)
{
$this->container["header-output"][] = $header;
return $this;
}
/**
* Set path to source image to.
*
* @param string $source path to source image file.
*
* @return $this
*/
public function setSource($source)
{
$this->container["source"] = $source;
return $this;
}
/**
* Set last modified of source image, use to check for 304.
*
* @param string $lastModified
*
* @return $this
*/
public function setLastModified($lastModified)
{
$this->container["last-modified"] = $lastModified;
return $this;
}
/**
* Get filename of cached item.
*
* @return string as filename.
*/
public function getFilename()
{
return $this->path . "/" . $this->filename;
}
/**
* Write current item to cache.
*
* @return boolean if cache file was written.
*/
public function writeToCache()
{
if (!$this->enabled) {
return false;
}
if (is_dir($this->path) && is_writable($this->path)) {
$filename = $this->getFilename();
return file_put_contents($filename, json_encode($this->container)) !== false;
}
return false;
}
/**
* Output current item from cache, if available.
*
* @return void
*/
public function output()
{
$filename = $this->getFilename();
if (!is_readable($filename)) {
return;
}
$item = json_decode(file_get_contents($filename), true);
if (!is_readable($item["source"])) {
return;
}
foreach ($item["header"] as $value) {
header($value);
}
if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])
&& strtotime($_SERVER["HTTP_IF_MODIFIED_SINCE"]) == $item["last-modified"]) {
header("HTTP/1.0 304 Not Modified");
if (CIMAGE_DEBUG) {
trace(__CLASS__ . " 304");
}
exit;
}
foreach ($item["header-output"] as $value) {
header($value);
}
if (CIMAGE_DEBUG) {
trace(__CLASS__ . " 200");
}
readfile($item["source"]);
exit;
}
}

View File

@@ -1,4 +1,7 @@
<?php
namespace Mos\CImage;
/**
* Get a image from a remote server using HTTP GET and If-Modified-Since.
*

View File

@@ -1,4 +1,7 @@
<?php
namespace Mos\CImage;
/**
* Resize and crop images on the fly, store generated images in a cache.
*
@@ -155,6 +158,13 @@ class CImage
/**
* Do lossy output using external postprocessing tools.
*/
private $lossy = null;
/**
* Verbose mode to print out a trace and display the created image
*/
@@ -190,7 +200,15 @@ class CImage
/**
* Path to command for filter optimize, for example optipng or null.
* Path to command for lossy optimize, for example pngquant.
*/
private $pngLossy;
private $pngLossyCmd;
/**
* Path to command for filter optimize, for example optipng.
*/
private $pngFilter;
private $pngFilterCmd;
@@ -198,7 +216,7 @@ class CImage
/**
* Path to command for deflate optimize, for example pngout or null.
* Path to command for deflate optimize, for example pngout.
*/
private $pngDeflate;
private $pngDeflateCmd;
@@ -208,8 +226,8 @@ class CImage
/**
* Path to command to optimize jpeg images, for example jpegtran or null.
*/
private $jpegOptimize;
private $jpegOptimizeCmd;
private $jpegOptimize;
private $jpegOptimizeCmd;
@@ -339,25 +357,33 @@ class CImage
/**
* Used with option area to set which parts of the image to use.
*/
private $area;
private $offset;
/**
* Calculate target dimension for image when using fill-to-fit resize strategy.
*/
* Calculate target dimension for image when using fill-to-fit resize strategy.
*/
private $fillWidth;
private $fillHeight;
/**
* Allow remote file download, default is to disallow remote file download.
*/
* Allow remote file download, default is to disallow remote file download.
*/
private $allowRemote = false;
/**
* Path to cache for remote download.
*/
private $remoteCache;
/**
* Pattern to recognize a remote file.
*/
@@ -372,6 +398,12 @@ class CImage
private $useCache = true;
/**
* Disable the fasttrackCacke to start with, inject an object to enable it.
*/
private $fastTrackCache = null;
/*
* Set whitelist for valid hostnames from where remote source can be
@@ -400,10 +432,15 @@ class CImage
*/
const RESIZE = 1;
const RESAMPLE = 2;
private $copyStrategy = NULL;
private $copyStrategy = null;
/*
* Class for image resizer.
*/
private $imageResizer = null;
/**
* Properties, the class is mutable and the method setOptions()
* decides (partly) what properties are created.
@@ -435,6 +472,27 @@ class CImage
{
$this->setSource($imageSrc, $imageFolder);
$this->setTarget($saveFolder, $saveName);
$this->imageResizer = new CImageResizer(array($this, 'log'));
}
/**
* Inject object and use it, must be available as member.
*
* @param string $property to set as object.
* @param object $object to set to property.
*
* @return $this
*/
public function injectDependency($property, $object)
{
if (!property_exists($this, $property)) {
$this->raiseError("Injecting unknown property.");
}
$this->$property = $object;
return $this;
}
@@ -512,13 +570,15 @@ class CImage
* Allow or disallow remote image download.
*
* @param boolean $allow true or false to enable and disable.
* @param string $cache path to cache dir.
* @param string $pattern to use to detect if its a remote file.
*
* @return $this
*/
public function setRemoteDownload($allow, $pattern = null)
public function setRemoteDownload($allow, $cache, $pattern = null)
{
$this->allowRemote = $allow;
$this->remoteCache = $cache;
$this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern;
$this->log(
@@ -606,7 +666,7 @@ class CImage
*/
private function checkFileExtension($extension)
{
$valid = array('jpg', 'jpeg', 'png', 'gif');
$valid = array('jpg', 'jpeg', 'png', 'gif', 'webp');
in_array(strtolower($extension), $valid)
or $this->raiseError('Not a valid file extension.');
@@ -629,7 +689,7 @@ class CImage
if ($extension == 'jpeg') {
$extension = 'jpg';
}
}
return $extension;
}
@@ -650,21 +710,12 @@ class CImage
}
$remote = new CRemoteImage();
$cache = $this->saveFolder . "/remote/";
if (!is_dir($cache)) {
if (!is_writable($this->saveFolder)) {
throw new Exception("Can not create remote cache, cachefolder not writable.");
}
mkdir($cache);
$this->log("The remote cache does not exists, creating it.");
}
if (!is_writable($cache)) {
if (!is_writable($this->remoteCache)) {
$this->log("The remote cache is not writable.");
}
$remote->setCache($cache);
$remote->setCache($this->remoteCache);
$remote->useCache($this->useCache);
$src = $remote->download($src);
@@ -802,6 +853,9 @@ class CImage
// Output format
'outputFormat' => null,
'dpr' => 1,
// Postprocessing using external tools
'lossy' => null,
);
// Convert crop settings from string to array
@@ -916,17 +970,36 @@ class CImage
is_readable($file)
or $this->raiseError('Image file does not exist.');
// Get details on image
$info = list($this->width, $this->height, $this->fileType, $this->attr) = getimagesize($file);
$info = list($this->width, $this->height, $this->fileType) = getimagesize($file);
if (empty($info)) {
throw new Exception("The file doesn't seem to be a valid image.");
// To support webp
$this->fileType = false;
if (function_exists("exif_imagetype")) {
$this->fileType = exif_imagetype($file);
if ($this->fileType === false) {
if (function_exists("imagecreatefromwebp")) {
$webp = imagecreatefromwebp($file);
if ($webp !== false) {
$this->width = imagesx($webp);
$this->height = imagesy($webp);
$this->fileType = IMG_WEBP;
}
}
}
}
}
if (!$this->fileType) {
throw new Exception("Loading image details, the file doesn't seem to be a valid image.");
}
$this->imageResizer->setSource($this->width, $this->height);
if ($this->verbose) {
$this->log("Loading image details for: {$file}");
$this->log("#Loading image details for: {$file}");
$this->log(" Image width x height (type): {$this->width} x {$this->height} ({$this->fileType}).");
$this->log(" Image filesize: " . filesize($file) . " bytes.");
$this->log(" Image mimetype: " . image_type_to_mime_type($this->fileType));
$this->log(" Image mimetype: " . $this->getMimeType());
}
return $this;
@@ -935,74 +1008,35 @@ class CImage
/**
* Init new width and height and do some sanity checks on constraints, before any
* processing can be done.
* Get mime type for image type.
*
* @return $this
* @throws Exception
*/
protected function getMimeType()
{
if ($this->fileType === IMG_WEBP) {
return "image/webp";
}
return image_type_to_mime_type($this->fileType);
}
/**
* Init new width and height and do some sanity checks on constraints,
* before anyprocessing can be done.
*
* @return $this
* @throws Exception
*/
public function initDimensions()
{
$this->log("Init dimension (before) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");
// width as %
if ($this->newWidth[strlen($this->newWidth)-1] == '%') {
$this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100;
$this->log("Setting new width based on % to {$this->newWidth}");
}
// height as %
if ($this->newHeight[strlen($this->newHeight)-1] == '%') {
$this->newHeight = $this->height * substr($this->newHeight, 0, -1) / 100;
$this->log("Setting new height based on % to {$this->newHeight}");
}
is_null($this->aspectRatio) or is_numeric($this->aspectRatio) or $this->raiseError('Aspect ratio out of range');
// width & height from aspect ratio
if ($this->aspectRatio && is_null($this->newWidth) && is_null($this->newHeight)) {
if ($this->aspectRatio >= 1) {
$this->newWidth = $this->width;
$this->newHeight = $this->width / $this->aspectRatio;
$this->log("Setting new width & height based on width & aspect ratio (>=1) to (w x h) {$this->newWidth} x {$this->newHeight}");
} else {
$this->newHeight = $this->height;
$this->newWidth = $this->height * $this->aspectRatio;
$this->log("Setting new width & height based on width & aspect ratio (<1) to (w x h) {$this->newWidth} x {$this->newHeight}");
}
} elseif ($this->aspectRatio && is_null($this->newWidth)) {
$this->newWidth = $this->newHeight * $this->aspectRatio;
$this->log("Setting new width based on aspect ratio to {$this->newWidth}");
} elseif ($this->aspectRatio && is_null($this->newHeight)) {
$this->newHeight = $this->newWidth / $this->aspectRatio;
$this->log("Setting new height based on aspect ratio to {$this->newHeight}");
}
// Change width & height based on dpr
if ($this->dpr != 1) {
if (!is_null($this->newWidth)) {
$this->newWidth = round($this->newWidth * $this->dpr);
$this->log("Setting new width based on dpr={$this->dpr} - w={$this->newWidth}");
}
if (!is_null($this->newHeight)) {
$this->newHeight = round($this->newHeight * $this->dpr);
$this->log("Setting new height based on dpr={$this->dpr} - h={$this->newHeight}");
}
}
// Check values to be within domain
is_null($this->newWidth)
or is_numeric($this->newWidth)
or $this->raiseError('Width not numeric');
is_null($this->newHeight)
or is_numeric($this->newHeight)
or $this->raiseError('Height not numeric');
$this->log("Init dimension (after) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");
$this->imageResizer->setBaseWidthHeight($this->newWidth, $this->newHeight)
->setBaseAspecRatio($this->aspectRatio)
->setBaseDevicePixelRate($this->dpr)
->prepareTargetDimensions();
return $this;
}
@@ -1016,6 +1050,31 @@ class CImage
*/
public function calculateNewWidthAndHeight()
{
$imres = $this->imageResizer;
$strategy = null;
$strategy = $imres::KEEP_RATIO;
if ($this->keepRatio == false) {
$strategy = $imres::STRETCH;
}
if ($this->cropToFit == true) {
$strategy = $imres::CROP_TO_FIT;
}
if ($this->fillToFit == true) {
$strategy = $imres::FILL_TO_FIT;
}
$imres->setResizeStrategy($strategy)
->allowUpscale($this->upscale)
->calculateTargetWidthAndHeight();
//$this->newWidth = $imres->getTargetWidth();
//$this->newHeight = $imres->getTargetHeight();
/*
// Crop, use cropped width and height as base for calulations
$this->log("Calculate new width and height.");
$this->log("Original width x height is {$this->width} x {$this->height}.");
@@ -1098,7 +1157,13 @@ class CImage
$this->newWidth = round($factor * $width);
$this->log("New height was set.");
} else {
// Use existing width and height as new width and height.
$this->newWidth = $width;
$this->newHeight = $height;
}
// Get image dimensions for pre-resize image.
if ($this->cropToFit || $this->fillToFit) {
@@ -1137,18 +1202,12 @@ class CImage
$this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']);
}
// Fill to fit, ensure to set new width and height
/*if ($this->fillToFit) {
$this->log("FillToFit.");
$this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->crop['width']);
$this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']);
}*/
// No new height or width is set, use existing measures.
$this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->width);
$this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->height);
$this->log("Calculated new width x height as {$this->newWidth} x {$this->newHeight}.");
*/
return $this;
}
@@ -1161,7 +1220,10 @@ class CImage
*/
public function reCalculateDimensions()
{
$this->log("Re-calculate image dimensions, newWidth x newHeigh was: " . $this->newWidth . " x " . $this->newHeight);
$this->log("Re-calculate image dimensions, newWidth x newHeigh was: "
. $this->newWidth
. " x "
. $this->newHeight);
$this->newWidth = $this->newWidthOrig;
$this->newHeight = $this->newHeightOrig;
@@ -1282,6 +1344,7 @@ class CImage
&& !$this->autoRotate
&& !$this->bgColor
&& ($this->upscale === self::UPSCALE_DEFAULT)
&& !$this->lossy
) {
$this->log("Using original image.");
$this->output($this->pathToImage);
@@ -1307,6 +1370,7 @@ class CImage
$filename = basename($this->pathToImage);
$cropToFit = $this->cropToFit ? '_cf' : null;
$fillToFit = $this->fillToFit ? '_ff' : null;
$stretch = $this->keepRatio === false ? '_st' : null;
$crop_x = $this->crop_x ? "_x{$this->crop_x}" : null;
$crop_y = $this->crop_y ? "_y{$this->crop_y}" : null;
$scale = $this->scale ? "_s{$this->scale}" : null;
@@ -1315,6 +1379,7 @@ class CImage
$compress = $this->compress ? "_co{$this->compress}" : null;
$rotateBefore = $this->rotateBefore ? "_rb{$this->rotateBefore}" : null;
$rotateAfter = $this->rotateAfter ? "_ra{$this->rotateAfter}" : null;
$lossy = $this->lossy ? "_l" : null;
$saveAs = $this->normalizeFileExtension();
$saveAs = $saveAs ? "_$saveAs" : null;
@@ -1374,14 +1439,14 @@ class CImage
$subdir = ($subdir == '.') ? '_.' : $subdir;
$subdir .= '_';
}
$file = $prefix . $subdir . $filename . $width . $height
. $offset . $crop . $cropToFit . $fillToFit
. $offset . $crop . $cropToFit . $fillToFit . $stretch
. $crop_x . $crop_y . $upscale
. $quality . $filters . $sharpen . $emboss . $blur . $palette
. $optimize . $compress
. $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor
. $convolve . $copyStrat . $saveAs;
. $convolve . $copyStrat . $lossy . $saveAs;
return $this->setTarget($file, $base);
}
@@ -1424,8 +1489,7 @@ class CImage
/**
* Load image from disk. Try to load image without verbose error message,
* if fail, load again and display error messages.
* Load image from disk.
*
* @param string $src of image.
* @param string $dir as base directory where images are.
@@ -1439,9 +1503,14 @@ class CImage
$this->setSource($src, $dir);
}
$this->loadImageDetails($this->pathToImage);
$this->loadImageDetails();
$this->image = imagecreatefromstring(file_get_contents($this->pathToImage));
if ($this->fileType === IMG_WEBP) {
$this->image = imagecreatefromwebp($this->pathToImage);
} else {
$imageAsString = file_get_contents($this->pathToImage);
$this->image = imagecreatefromstring($imageAsString);
}
if ($this->image === false) {
throw new Exception("Could not load image.");
}
@@ -1520,7 +1589,6 @@ class CImage
}
switch ($pngType) {
case self::PNG_GREYSCALE:
$text = "PNG is type 0, Greyscale$transparent";
break;
@@ -1626,11 +1694,11 @@ class CImage
*
* @return $this
*/
public function setCopyResizeStrategy($strategy)
{
$this->copyStrategy = $strategy;
return $this;
}
public function setCopyResizeStrategy($strategy)
{
$this->copyStrategy = $strategy;
return $this;
}
@@ -1641,7 +1709,7 @@ class CImage
*/
public function imageCopyResampled($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
{
if($this->copyStrategy == self::RESIZE) {
if ($this->copyStrategy == self::RESIZE) {
$this->log("Copy by resize");
imagecopyresized($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
} else {
@@ -1659,13 +1727,35 @@ class CImage
*/
public function resize()
{
$res = $this->imageResizer;
$this->log("### Starting to Resize()");
$this->log("Upscale = '$this->upscale'");
$this->log(" Upscale = '$this->upscale'");
$sw = $res->getSourceWidth();
$sh = $res->getSourceHeight();
$tw = $res->getTargetWidth();
$th = $res->getTargetHeight();
$cx = $res->getCropX();
$cy = $res->getCropY();
$cw = $res->getCropWidth();
$ch = $res->getCropHeight();
$dx = $res->getDestinationX();
$dy = $res->getDestinationY();
$dw = $res->getDestinationWidth();
$dh = $res->getDestinationHeight();
$img = $this->CreateImageKeepTransparency($tw, $th);
$this->imageCopyResampled($img, $this->image, $dx, $dy, $cx, $cy, $dw, $dh, $cw, $ch);
$this->image = $img;
$this->width = $tw;
$this->height = $th;
return $this;
// Only use a specified area of the image, $this->offset is defining the area to use
if (isset($this->offset)) {
$this->log("Offset for area to use, cropping it width={$this->offset['width']}, height={$this->offset['height']}, start_x={$this->offset['left']}, start_y={$this->offset['top']}");
$img = $this->CreateImageKeepTransparency($this->offset['width'], $this->offset['height']);
imagecopy($img, $this->image, 0, 0, $this->offset['left'], $this->offset['top'], $this->offset['width'], $this->offset['height']);
@@ -1675,7 +1765,6 @@ class CImage
}
if ($this->crop) {
// Do as crop, take only part of image
$this->log("Cropping area width={$this->crop['width']}, height={$this->crop['height']}, start_x={$this->crop['start_x']}, start_y={$this->crop['start_y']}");
$img = $this->CreateImageKeepTransparency($this->crop['width'], $this->crop['height']);
@@ -1692,44 +1781,67 @@ class CImage
}
if ($this->cropToFit) {
// Resize by crop to fit
$this->log("Resizing using strategy - Crop to fit");
if (!$this->upscale && ($this->width < $this->newWidth || $this->height < $this->newHeight)) {
if (!$this->upscale
&& ($this->width < $this->newWidth || $this->height < $this->newHeight)) {
$this->log("Resizing - smaller image, do not upscale.");
$cropX = round(($this->cropWidth/2) - ($this->newWidth/2));
$cropY = round(($this->cropHeight/2) - ($this->newHeight/2));
$posX = 0;
$posY = 0;
$cropX = 0;
$cropY = 0;
if ($this->newWidth > $this->width) {
$posX = round(($this->newWidth - $this->width) / 2);
}
if ($this->newWidth < $this->width) {
$cropX = round(($this->width/2) - ($this->newWidth/2));
}
if ($this->newHeight > $this->height) {
$posY = round(($this->newHeight - $this->height) / 2);
}
if ($this->newHeight < $this->height) {
$cropY = round(($this->height/2) - ($this->newHeight/2));
}
$this->log(" cwidth: $this->cropWidth");
$this->log(" cheight: $this->cropHeight");
$this->log(" nwidth: $this->newWidth");
$this->log(" nheight: $this->newHeight");
$this->log(" width: $this->width");
$this->log(" height: $this->height");
$this->log(" posX: $posX");
$this->log(" posY: $posY");
$this->log(" cropX: $cropX");
$this->log(" cropY: $cropY");
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopy($imageResized, $this->image, $posX, $posY, $cropX, $cropY, $this->newWidth, $this->newHeight);
imagecopy($imageResized, $this->image, $posX, $posY, $cropX, $cropY, $this->width, $this->height);
} else {
$cropX = round(($this->cropWidth/2) - ($this->newWidth/2));
$cropY = round(($this->cropHeight/2) - ($this->newHeight/2));
$imgPreCrop = $this->CreateImageKeepTransparency($this->cropWidth, $this->cropHeight);
$cropX = $imgres->getCropX();
$cropY = $imgres->getCropY();
$cropWidth = $imgres->getCropWidth();
$cropHeight = $imgres->getCropHeight();
$this->log(" Crop from $cropX x $cropY by $cropWidth x $cropHeight.");
//$cropX = round(($this->cropWidth/2) - ($this->newWidth/2));
//$cropY = round(($this->cropHeight/2) - ($this->newHeight/2));
//$imgPreCrop = $this->CreateImageKeepTransparency($this->cropWidth, $this->cropHeight);
$imgPreCrop = $this->CreateImageKeepTransparency($cropWidth, $cropHeight);
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
$this->imageCopyResampled($imgPreCrop, $this->image, 0, 0, 0, 0, $this->cropWidth, $this->cropHeight, $this->width, $this->height);
// $this->imageCopyResampled($imgPreCrop, $this->image, 0, 0, 0, 0, $this->cropWidth, $this->cropHeight, $this->width, $this->height);
$this->imageCopyResampled($imgPreCrop, $this->image, 0, 0, 0, 0, $cropWidth, $cropHeight, $this->width, $this->height);
imagecopy($imageResized, $imgPreCrop, 0, 0, $cropX, $cropY, $this->newWidth, $this->newHeight);
}
$this->image = $imageResized;
$this->width = $this->newWidth;
$this->height = $this->newHeight;
} elseif ($this->fillToFit) {
// Resize by fill to fit
$this->log("Resizing using strategy - Fill to fit");
@@ -1747,15 +1859,13 @@ class CImage
}
if (!$this->upscale
&& ($this->width < $this->newWidth || $this->height < $this->newHeight)
&& ($this->width < $this->newWidth && $this->height < $this->newHeight)
) {
$this->log("Resizing - smaller image, do not upscale.");
$posX = round(($this->fillWidth - $this->width) / 2);
$posY = round(($this->fillHeight - $this->height) / 2);
$posX = round(($this->newWidth - $this->width) / 2);
$posY = round(($this->newHeight - $this->height) / 2);
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopy($imageResized, $this->image, $posX, $posY, 0, 0, $this->fillWidth, $this->fillHeight);
imagecopy($imageResized, $this->image, $posX, $posY, 0, 0, $this->width, $this->height);
} else {
$imgPreFill = $this->CreateImageKeepTransparency($this->fillWidth, $this->fillHeight);
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
@@ -1766,9 +1876,7 @@ class CImage
$this->image = $imageResized;
$this->width = $this->newWidth;
$this->height = $this->newHeight;
} elseif (!($this->newWidth == $this->width && $this->newHeight == $this->height)) {
// Resize it
$this->log("Resizing, new height and/or width");
@@ -1796,9 +1904,8 @@ class CImage
$cropX = round(($this->width - $this->newWidth) / 2);
}
//$this->log("posX=$posX, posY=$posY, cropX=$cropX, cropY=$cropY.");
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopy($imageResized, $this->image, $posX, $posY, $cropX, $cropY, $this->newWidth, $this->newHeight);
imagecopy($imageResized, $this->image, $posX, $posY, $cropX, $cropY, $this->width, $this->height);
$this->image = $imageResized;
$this->width = $this->newWidth;
$this->height = $this->newHeight;
@@ -1834,12 +1941,10 @@ class CImage
// Apply filters
if (isset($this->filters) && is_array($this->filters)) {
foreach ($this->filters as $filter) {
$this->log("Applying filter {$filter['type']}.");
switch ($filter['argc']) {
case 0:
imagefilter($this->image, $filter['type']);
break;
@@ -2184,7 +2289,6 @@ class CImage
$img = isset($img) ? $img : $this->image;
if ($this->bgColorDefault) {
$red = $this->bgColorDefault['red'];
$green = $this->bgColorDefault['green'];
$blue = $this->bgColorDefault['blue'];
@@ -2197,7 +2301,6 @@ class CImage
}
return $color;
} else {
return 0;
}
@@ -2225,16 +2328,13 @@ class CImage
: -1;
if ($index != -1) {
imagealphablending($img, true);
$transparent = imagecolorsforindex($this->image, $index);
$color = imagecolorallocatealpha($img, $transparent['red'], $transparent['green'], $transparent['blue'], $transparent['alpha']);
imagefill($img, 0, 0, $color);
$index = imagecolortransparent($img, $color);
$this->Log("Detected transparent color = " . implode(", ", $transparent) . " at index = $index");
} elseif ($this->bgColorDefault) {
$color = $this->getBackgroundColor($img);
imagefill($img, 0, 0, $color);
$this->Log("Filling image with background color.");
@@ -2260,6 +2360,14 @@ class CImage
$this->jpegOptimizeCmd = null;
}
if (array_key_exists("png_lossy", $options)
&& $options['png_lossy'] !== false) {
$this->pngLossy = $options['png_lossy'];
$this->pngLossyCmd = $options['png_lossy_cmd'];
} else {
$this->pngLossyCmd = null;
}
if (isset($options['png_filter']) && $options['png_filter']) {
$this->pngFilterCmd = $options['png_filter_cmd'];
} else {
@@ -2287,9 +2395,11 @@ class CImage
// switch on mimetype
if (isset($this->extension)) {
return strtolower($this->extension);
} else {
return substr(image_type_to_extension($this->fileType), 1);
} elseif ($this->fileType === IMG_WEBP) {
return "webp";
}
return substr(image_type_to_extension($this->fileType), 1);
}
@@ -2319,8 +2429,7 @@ class CImage
$type = $this->getTargetImageExtension();
$this->Log("Saving image as " . $type);
switch($type) {
switch ($type) {
case 'jpeg':
case 'jpg':
$this->Log("Saving image as JPEG to cache using quality = {$this->quality}.");
@@ -2345,6 +2454,11 @@ class CImage
imagegif($this->image, $this->cacheFileName);
break;
case 'webp':
$this->Log("Saving image as WEBP to cache using quality = {$this->quality}.");
imagewebp($this->image, $this->cacheFileName, $this->quality);
break;
case 'png':
default:
$this->Log("Saving image as PNG to cache using compression = {$this->compress}.");
@@ -2354,6 +2468,24 @@ class CImage
imagesavealpha($this->image, true);
imagepng($this->image, $this->cacheFileName, $this->compress);
// Use external program to process lossy PNG, if defined
$lossyEnabled = $this->pngLossy === true;
$lossySoftEnabled = $this->pngLossy === null;
$lossyActiveEnabled = $this->lossy === true;
if ($lossyEnabled || ($lossySoftEnabled && $lossyActiveEnabled)) {
if ($this->verbose) {
clearstatcache();
$this->log("Lossy enabled: $lossyEnabled");
$this->log("Lossy soft enabled: $lossySoftEnabled");
$this->Log("Filesize before lossy optimize: " . filesize($this->cacheFileName) . " bytes.");
}
$res = array();
$cmd = $this->pngLossyCmd . " $this->cacheFileName $this->cacheFileName";
exec($cmd, $res);
$this->Log($cmd);
$this->Log($res);
}
// Use external program to filter PNG, if defined
if ($this->pngFilterCmd) {
if ($this->verbose) {
@@ -2417,7 +2549,7 @@ class CImage
$this->log("# Converting image to sRGB colorspace.");
}
if (!class_exists("Imagick")) {
if (!class_exists("\Imagick")) {
$this->log(" Ignoring since Imagemagick is not installed.");
return false;
}
@@ -2438,10 +2570,10 @@ class CImage
}
}
// Only covert if cachedir is writable
// Only convert if cachedir is writable
if (is_writable($this->saveFolder)) {
// Load file and check if conversion is needed
$image = new Imagick($this->pathToImage);
$image = new \Imagick($this->pathToImage);
$colorspace = $image->getImageColorspace();
$this->log(" Current colorspace: " . $colorspace);
@@ -2449,13 +2581,13 @@ class CImage
$hasICCProfile = (array_search('icc', $profiles) !== false);
$this->log(" Has ICC color profile: " . ($hasICCProfile ? "YES" : "NO"));
if ($colorspace != Imagick::COLORSPACE_SRGB || $hasICCProfile) {
if ($colorspace != \Imagick::COLORSPACE_SRGB || $hasICCProfile) {
$this->log(" Converting to sRGB.");
$sRGBicc = file_get_contents($iccFile);
$image->profileImage('icc', $sRGBicc);
$image->transformImageColorspace(Imagick::COLORSPACE_SRGB);
$image->transformImageColorspace(\Imagick::COLORSPACE_SRGB);
$image->writeImage($this->cacheFileName);
return $this->cacheFileName;
}
@@ -2499,7 +2631,7 @@ class CImage
/**
* Add HTTP header for putputting together with image.
* Add HTTP header for output together with image.
*
* @param string $type the header type such as "Cache-Control"
* @param string $value the value to use
@@ -2533,6 +2665,7 @@ class CImage
$format = $this->outputFormat;
}
$this->log("### Output");
$this->log("Output format is: $format");
if (!$this->verbose && $format == 'json') {
@@ -2550,18 +2683,24 @@ class CImage
// Get image modification time
clearstatcache();
$lastModified = filemtime($file);
$gmdate = gmdate("D, d M Y H:i:s", $lastModified);
$lastModifiedFormat = "D, d M Y H:i:s";
$gmdate = gmdate($lastModifiedFormat, $lastModified);
if (!$this->verbose) {
header('Last-Modified: ' . $gmdate . " GMT");
$header = "Last-Modified: $gmdate GMT";
header($header);
$this->fastTrackCache->addHeader($header);
$this->fastTrackCache->setLastModified($lastModified);
}
foreach($this->HTTPHeader as $key => $val) {
header("$key: $val");
foreach ($this->HTTPHeader as $key => $val) {
$header = "$key: $val";
header($header);
$this->fastTrackCache->addHeader($header);
}
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) {
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
&& strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) {
if ($this->verbose) {
$this->log("304 not modified");
$this->verboseOutput();
@@ -2569,13 +2708,12 @@ class CImage
}
header("HTTP/1.0 304 Not Modified");
if (CIMAGE_DEBUG) {
trace(__CLASS__ . " 304");
}
} else {
// Get details on image
$info = getimagesize($file);
!empty($info) or $this->raiseError("The file doesn't seem to be an image.");
$mime = $info['mime'];
$this->loadImageDetails($file);
$mime = $this->getMimeType();
$size = filesize($file);
if ($this->verbose) {
@@ -2589,8 +2727,19 @@ class CImage
}
}
header("Content-type: $mime");
header("Content-length: $size");
$header = "Content-type: $mime";
header($header);
$this->fastTrackCache->addHeaderOnOutput($header);
$header = "Content-length: $size";
header($header);
$this->fastTrackCache->addHeaderOnOutput($header);
$this->fastTrackCache->setSource($file);
$this->fastTrackCache->writeToCache();
if (CIMAGE_DEBUG) {
trace(__CLASS__ . " 200");
}
readfile($file);
}
@@ -2625,7 +2774,7 @@ class CImage
$this->load($file);
$details['filename'] = basename($file);
$details['mimeType'] = image_type_to_mime_type($this->fileType);
$details['mimeType'] = $this->getMimeType($this->fileType);
$details['width'] = $this->width;
$details['height'] = $this->height;
$details['aspectRatio'] = round($this->width / $this->height, 3);
@@ -2727,6 +2876,7 @@ class CImage
private function verboseOutput()
{
$log = null;
$this->log("### Summary of verbose log");
$this->log("As JSON: \n" . $this->json());
$this->log("Memory peak: " . round(memory_get_peak_usage() /1024/1024) . "M");
$this->log("Memory limit: " . ini_get('memory_limit'));

View File

@@ -0,0 +1,838 @@
<?php
namespace Mos\CImage;
/**
* Resize and crop images.
*
* @author Mikael Roos mos@dbwebb.se
* @example http://dbwebb.se/opensource/cimage
* @link https://github.com/mosbth/cimage
*/
class CImageResizer
{
/**
* Log function.
*/
private $log;
/**
* Source image dimensions, calculated from loaded image.
*/
private $srcWidth;
private $srcHeight;
/**
* Set as expected target image/canvas dimensions.
*/
private $targetWidth;
private $targetHeight;
/**
* Where should the image go on the canvas.
*/
private $destinationX;
private $destinationY;
private $destinationWidth;
private $destinationHeight;
/**
* Which parts to crop from the source.
*/
private $cropX;
private $cropY;
private $cropWidth;
private $cropHeight;
/**
* Change target height & width when different dpr, dpr 2 means double
* image dimensions.
*/
private $dpr = null;
/**
* Set aspect ratio for the target image.
*/
private $aspectRatio;
/**
* Array with details on how to crop.
* Array contains xxxxxx
*/
public $crop;
public $cropOrig; // Save original value?
/**
* Area to use for target image, crop out parts not in area.
* Array with top, right, bottom, left percentage values to crop out.
*/
private $area;
/**
* Pixel offset in source image to decide which part of image is used.
* Array with top, right, bottom, left percentage values to crop out.
*/
private $offset;
/**
* Resize strategy, image should keep its original ratio.
*/
const KEEP_RATIO = 1;
/**
* Resize strategy, image should crop and fill area.
*/
const CROP_TO_FIT = 2;
/**
* Resize strategy, image should fit in area and fill remains.
*/
const FILL_TO_FIT = 3;
/**
* Resize strategy, image should stretch to fit in area.
*/
const STRETCH = 4;
/**
* The currently selected resize strategy.
*/
private $resizeStrategy = self::KEEP_RATIO;
/**
* Allow upscale of smaller images by default, set to false to disallow.
*/
private $upscale = true;
/**
* Constructor, set log function to use for verbose logging or null
* to disable logging.
*
* @param callable $log function to call for logging.
*/
public function __construct($log = null)
{
$this->log = $log;
}
/**
* Log string using logger.
*
* @param string $str to log.
*/
public function log($str)
{
if ($this->log) {
call_user_func($this->log, $str);
}
}
/**
* Set source dimensions.
*
* @param integer $width of source image.
* @param integer $height of source image.
*
* @throws Exception
*
* @return $this
*/
public function setSource($width, $height)
{
$this->srcWidth = $width;
$this->srcHeight = $height;
$this->log("# Source image dimension: {$this->srcWidth}x{$this->srcHeight}.");
return $this;
}
/**
* Get resize strategy as string.
*
* @return string
*/
public function getResizeStrategyAsString()
{
switch ($this->resizeStrategy) {
case self::KEEP_RATIO:
return "KEEP_RATIO";
break;
case self::CROP_TO_FIT:
return "CROP_TO_FIT";
break;
case self::FILL_TO_FIT:
return "FILL_TO_FIT";
break;
case self::STRETCH:
return "STRETCH";
break;
default:
return "UNKNOWN";
}
}
/**
* Set resize strategy as KEEP_RATIO, CROP_TO_FIT or FILL_TO_FIT.
*
* @param integer $strategy
*
* @return $this
*/
public function setResizeStrategy($strategy)
{
$this->resizeStrategy = $strategy;
$this->log("# Resize strategy is " . $this->getResizeStrategyAsString());
return $this;
}
/**
* Allow or disallow upscale smaller images.
*
* @param boolean $upscale
*
* @return $this
*/
public function allowUpscale($upscale)
{
$this->upscale = $upscale;
$this->log("# Allow upscale is $this->upscale.");
return $this;
}
/**
* Check if a value is upscaled or not by compare $current to $orig,
* if $current is larger than $orig then check if upscaled is allowed.
*
* @param float &$current value to check and change.
* @param float $orig value to check against.
*
* @return boolean true if upscaled changed values, else false.
*/
public function respectUpscale(&$current, $orig)
{
if (!$this->upscale && $current > $orig) {
$this->log("# Disallowed upscale to $current ($orig)");
$current = $orig;
return true;
}
return false;
}
/**
* Set base for requested width and height.
*
* @param numeric|null $width as requested target width
* @param numeric|null $height as requested target height
*
* @throws Exception
*
* @return $this
*/
public function setBaseWidthHeight($width = null, $height = null)
{
$this->log("# Set base for width and height.");
$this->targetWidth = $width;
$this->targetHeight = $height;
// Width specified as %
if ($this->targetWidth[strlen($this->targetWidth)-1] == '%') {
$this->targetWidth = $this->srcWidth * substr($this->targetWidth, 0, -1) / 100;
$this->log(" Setting new width based on $width to {$this->targetWidth}.");
}
// Height specified as %
if ($this->targetHeight[strlen($this->targetHeight)-1] == '%') {
$this->targetHeight = $this->srcHeight * substr($this->targetHeight, 0, -1) / 100;
$this->log(" Setting new height based on $height to {$this->targetHeight}.");
}
if (!(is_null($this->targetWidth) || is_numeric($this->targetWidth))) {
throw new Exception('Width not numeric');
}
if (!(is_null($this->targetHeight) || is_numeric($this->targetHeight))) {
throw new Exception('Height not numeric');
}
$this->log(" Requested target dimension as: {$this->targetWidth}x{$this->targetHeight}.");
return $this;
}
/**
* Set base for requested aspect ratio.
*
* @param float|null $aspectRatio as requested aspect ratio
*
* @throws Exception
*
* @return $this
*/
public function setBaseAspecRatio($aspectRatio = null)
{
$this->log("# Set base for aspect ratio.");
$this->aspectRatio = $aspectRatio;
if (!(is_null($this->aspectRatio) || is_numeric($this->aspectRatio))) {
throw new Exception("Aspect ratio out of range");
}
$this->log(" Requested aspectRatio={$this->aspectRatio}.");
return $this;
}
/**
* Set base for requested device pixel ratio.
*
* @param float $dpr as requested density pixel rate
*
* @throws Exception
*
* @return $this
*/
public function setBaseDevicePixelRate($dpr = null)
{
$this->log("# Set base for device pixel rate.");
$this->dpr = $dpr;
if (!(is_null($dpr) || (is_numeric($this->dpr) && $this->dpr > 0))) {
throw new Exception("Device pixel rate out of range");
}
$this->log(" Requested dpr={$this->dpr}.");
return $this;
}
/**
* Calculate target width and height by considering the selected
* aspect ratio.
*
* @throws Exception
*
* @return $this
*/
public function prepareByConsiderAspectRatio()
{
$this->log(" Prepare by aspect ratio {$this->aspectRatio}.");
if (is_null($this->aspectRatio)) {
return $this;
}
// Both null, use source as base for target
if (is_null($this->targetWidth) && is_null($this->targetHeight)) {
$this->targetWidth = ($this->aspectRatio >= 1)
? $this->srcWidth
: null;
$this->targetHeight = ($this->aspectRatio >= 1)
? null
: $this->srcHeight;
$this->log(" Using source as base {$this->targetWidth}x{$this->targetHeight}");
}
// Both or either set, calculate the other
if (isset($this->targetWidth) && isset($this->targetHeight)) {
$this->targetWidth = ($this->aspectRatio >= 1)
? $this->targetWidth
: $this->targetHeight * $this->aspectRatio;
$this->targetHeight = ($this->aspectRatio >= 1)
? $this->targetWidth / $this->aspectRatio
: $this->targetHeight;
$this->log(" New target width height {$this->targetWidth}x{$this->targetHeight}");
} elseif (isset($this->targetWidth)) {
$this->targetHeight = $this->targetWidth / $this->aspectRatio;
$this->log(" New target height x{$this->targetHeight}");
} elseif (isset($this->targetHeight)) {
$this->targetWidth = $this->targetHeight * $this->aspectRatio;
$this->log(" New target width {$this->targetWidth}x");
}
return $this;
}
/**
* Calculate target width and height by considering the selected
* dpr.
*
* @throws Exception
*
* @return $this
*/
public function prepareByConsiderDpr()
{
$this->log(" Prepare by dpr={$this->dpr}.");
if (is_null($this->dpr)) {
return $this;
}
// If both not set, use source as base
if (is_null($this->targetWidth) && is_null($this->targetHeight)) {
$this->targetWidth = $this->srcWidth;
$this->targetHeight = $this->srcHeight;
}
if (isset($this->targetWidth)) {
$this->targetWidth = $this->targetWidth * $this->dpr;
$this->log(" Update target width to {$this->targetWidth}.");
}
if (isset($this->targetHeight)) {
$this->targetHeight = $this->targetHeight * $this->dpr;
$this->log(" Update target height to {$this->targetHeight}.");
}
return $this;
}
/**
* Calculate target width and height and do sanity checks on constraints.
* After this method the $targetWidth and $targetHeight will have
* the expected dimensions on the target image.
*
* @throws Exception
*
* @return $this
*/
public function prepareTargetDimensions()
{
$this->log("# Prepare target dimension (before): {$this->targetWidth}x{$this->targetHeight}.");
$this->prepareByConsiderAspectRatio()
->prepareByConsiderDpr();
$this->log(" Prepare target dimension (after): {$this->targetWidth}x{$this->targetHeight}.");
return $this;
}
/**
* Calculate new width and height of image.
*
* @return $this
*/
public function calculateTargetWidthAndHeight()
{
$this->log("# Calculate new width and height.");
$this->log(" Source size {$this->srcWidth}x{$this->srcHeight}.");
$this->log(" Target dimension (before) {$this->targetWidth}x{$this->targetHeight}.");
// Set default values to crop area to be whole source image
$sw = $this->srcWidth;
$sh = $this->srcHeight;
$ar = $sw / $sh;
$tw = $this->targetWidth;
$th = $this->targetHeight;
$dx = 0;
$dy = 0;
$dw = null;
$dh = null;
$cx = 0;
$cy = 0;
$cw = $sw;
$ch = $sh;
$rs = $this->resizeStrategy;
$both = isset($tw) && isset($th);
$ratio = $both ? $tw / $th : null;
if (is_null($tw) && is_null($th)) {
// No tw/th use sw/sh
$tw = $sw;
$th = $sh;
$this->log(" New tw x th {$tw}x{$th}");
} elseif (isset($tw) && is_null($th)) {
// Keep aspect ratio, make th based on tw
$this->respectUpscale($tw, $sw);
$th = $tw / $ar;
$this->log(" New th x{$th}");
} elseif (is_null($tw) && isset($th)) {
// Keep aspect ratio, make tw based on th
$this->respectUpscale($th, $sh);
$tw = $th * $ar;
$this->log(" New tw {$tw}x");
} elseif ($rs === CImageResizer::KEEP_RATIO && $both) {
// Keep aspect ratio, make fit in box not larger than tw/th
$this->log(" Keep ratio, ratio target=$ratio, source=$ar");
if ($ratio > $ar) {
$this->respectUpscale($th, $sh);
$tw = $th * $ar;
$this->log(" New tw {$tw}x");
} elseif ($ratio < $ar) {
$this->respectUpscale($tw, $sw);
$th = $tw / $ar;
$this->log(" New th x{$th}");
} else {
$this->respectUpscale($tw, $sw);
$this->respectUpscale($th, $sh);
}
} elseif ($rs === CImageResizer::STRETCH && $both) {
// Stretch to fit, leave as is
$this->log(" Stretch");
// respectUpscale
$dw = $tw;
$dh = $th;
$this->respectUpscale($dw, $sw);
$this->respectUpscale($dh, $sh);
$dx = ($tw - $dw) / 2;
$dy = ($th - $dh) / 2;
$this->log(" Destination area dx=$dx, dy=$dy, dw=$dw, dh=$dh");
} elseif ($rs === CImageResizer::CROP_TO_FIT && $both) {
// Crop to fit image in box
$this->log(" Crop to fit, ratio target=$ratio, source=$ar");
// Respect upscale
$dw = $tw;
$dh = $th;
$this->respectUpscale($dw, $sw);
$this->respectUpscale($dh, $sh);
$dx = ($tw - $dw) / 2;
$dy = ($th - $dh) / 2;
// Manage landscape/portrait
if ($ratio > $ar) {
$ch = $sw / $ratio;
$cy = ($sh - $ch) / 2;
$this->log(" Crop by cy=$cy ch=$ch");
} elseif ($ratio < $ar) {
$cw = $sh * $ratio;
$cx = ($sw - $cw) / 2;
$this->log(" Crop by cx=$cx cw=$cw");
}
// Update crop when no upscale
if (!$this->upscale && $dx) {
$cy = $th < $sh ? ($sh - $th) / 2 : 0;
$ch = $dh;
}
if (!$this->upscale && $dy) {
$cx = $tw < $sw ? ($sw - $tw) / 2 : 0;
$cw = $dw;
}
$this->log(" Parts cx=$cx, cy=$cy, cw=$cw, ch=$ch");
$this->log(" Destination area dx=$dx, dy=$dy, dw=$dw, dh=$dh");
} elseif ($rs === CImageResizer::FILL_TO_FIT && $both) {
// Fill to fit image in box
$this->log(" Fill to fit, ratio target=$ratio, source=$ar");
$dw = $tw;
$dh = $th;
// Manage landscape/portrait
if ($ratio > $ar) {
$dw = $th * $ar;
$dh = $th;
} elseif ($ratio < $ar) {
$dw = $tw;
$dh = $tw / $ar;
}
$this->respectUpscale($dw, $sw);
$this->respectUpscale($dh, $sh);
$dx = ($tw - $dw) / 2;
$dy = ($th - $dh) / 2;
$this->log(" Destination area dx=$dx, dy=$dy, dw=$dw, dh=$dh");
}
// All done, sum it up
$dw = is_null($dw) ? $tw : $dw;
$dh = is_null($dh) ? $th : $dh;
$this->targetWidth = round($tw);
$this->targetHeight = round($th);
$this->destinationX = round($dx);
$this->destinationY = round($dy);
$this->destinationWidth = round($dw);
$this->destinationHeight = round($dh);
$this->cropX = round($cx);
$this->cropY = round($cy);
$this->cropWidth = round($cw);
$this->cropHeight = round($ch);
$str = <<<EOD
Target dimension (after) {$this->targetWidth}x{$this->targetHeight}.
Crop area {$this->cropX}x{$this->cropY} by {$this->cropWidth}x{$this->cropHeight}.
Destination area {$this->destinationX}x{$this->destinationY} by {$this->destinationWidth}x{$this->destinationHeight}.
EOD;
$this->log($str);
/*
// Check if there is an area to crop off
if (isset($this->area)) {
$this->offset['top'] = round($this->area['top'] / 100 * $this->srcHeight);
$this->offset['right'] = round($this->area['right'] / 100 * $this->srcWidth);
$this->offset['bottom'] = round($this->area['bottom'] / 100 * $this->srcHeight);
$this->offset['left'] = round($this->area['left'] / 100 * $this->srcWidth);
$this->offset['width'] = $this->srcWidth - $this->offset['left'] - $this->offset['right'];
$this->offset['height'] = $this->srcHeight - $this->offset['top'] - $this->offset['bottom'];
$this->srcWidth = $this->offset['width'];
$this->srcHeight = $this->offset['height'];
$this->log("The offset for the area to use is top {$this->area['top']}%, right {$this->area['right']}%, bottom {$this->area['bottom']}%, left {$this->area['left']}%.");
$this->log("The offset for the area to use is top {$this->offset['top']}px, right {$this->offset['right']}px, bottom {$this->offset['bottom']}px, left {$this->offset['left']}px, width {$this->offset['width']}px, height {$this->offset['height']}px.");
}
// Check if crop is set
if ($this->crop) {
$width = $this->crop['width'] = $this->crop['width'] <= 0 ? $this->srcWidth + $this->crop['width'] : $this->crop['width'];
$height = $this->crop['height'] = $this->crop['height'] <= 0 ? $this->srcHeight + $this->crop['height'] : $this->crop['height'];
if ($this->crop['start_x'] == 'left') {
$this->crop['start_x'] = 0;
} elseif ($this->crop['start_x'] == 'right') {
$this->crop['start_x'] = $this->srcWidth - $width;
} elseif ($this->crop['start_x'] == 'center') {
$this->crop['start_x'] = round($this->srcWidth / 2) - round($width / 2);
}
if ($this->crop['start_y'] == 'top') {
$this->crop['start_y'] = 0;
} elseif ($this->crop['start_y'] == 'bottom') {
$this->crop['start_y'] = $this->srcHeight - $height;
} elseif ($this->crop['start_y'] == 'center') {
$this->crop['start_y'] = round($this->srcHeight / 2) - round($height / 2);
}
$this->log(" Crop area is width {$width}px, height {$height}px, start_x {$this->crop['start_x']}px, start_y {$this->crop['start_y']}px.");
}
// Crop, ensure to set new width and height
if ($this->crop) {
$this->log(" Crop.");
$this->targetWidth = round(isset($this->targetWidth)
? $this->targetWidth
: $this->crop['width']);
$this->targetHeight = round(isset($this->targetHeight)
? $this->targetHeight
: $this->crop['height']);
}
*/
return $this;
}
/**
* Get source width.
*
* @return integer as source width
*/
public function getSourceWidth()
{
return $this->srcWidth;
}
/**
* Get source height.
*
* @return integer as source height
*/
public function getSourceHeight()
{
return $this->srcHeight;
}
/**
* Get target width.
*
* @return integer as target width
*/
public function getTargetWidth()
{
return $this->targetWidth;
}
/**
* Get target height.
*
* @return integer as target height
*/
public function getTargetHeight()
{
return $this->targetHeight;
}
/**
* Get destination x.
*
* @return integer as destination x
*/
public function getDestinationX()
{
return $this->destinationX;
}
/**
* Get destination y.
*
* @return integer as destination y
*/
public function getDestinationY()
{
return $this->destinationY;
}
/**
* Get destination width.
*
* @return integer as destination width
*/
public function getDestinationWidth()
{
return $this->destinationWidth;
}
/**
* Get destination height.
*
* @return integer as destination height
*/
public function getDestinationHeight()
{
return $this->destinationHeight;
}
/**
* Get crop position x.
*
* @return integer
*/
public function getCropX()
{
return $this->cropX;
}
/**
* Get crop position y.
*
* @return integer
*/
public function getCropY()
{
return $this->cropY;
}
/**
* Get crop width.
*
* @return integer
*/
public function getCropWidth()
{
return $this->cropWidth;
}
/**
* Get crop height.
*
* @return integer
*/
public function getCropHeight()
{
return $this->cropHeight;
}
}

View File

@@ -1,4 +1,7 @@
<?php
namespace Mos\CImage;
/**
* Get a image from a remote server using HTTP GET and If-Modified-Since.
*
@@ -101,7 +104,7 @@ class CRemoteImage
*/
public function setCache($path)
{
$this->saveFolder = $path;
$this->saveFolder = rtrim($path, "/") . "/";
return $this;
}
@@ -147,7 +150,12 @@ class CRemoteImage
*/
public function setHeaderFields()
{
$this->http->setHeader("User-Agent", "CImage/0.7.2 (PHP/". phpversion() . " cURL)");
$cimageVersion = "CImage";
if (defined("CIMAGE_USER_AGENT")) {
$cimageVersion = CIMAGE_USER_AGENT;
}
$this->http->setHeader("User-Agent", "$cimageVersion (PHP/". phpversion() . " cURL)");
$this->http->setHeader("Accept", "image/jpeg,image/png,image/gif");
if ($this->useCache) {

View File

@@ -1,4 +1,7 @@
<?php
namespace Mos\CImage;
/**
* Act as whitelist (or blacklist).
*

22
src/CImage/Exception.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
namespace Mos\CImage;
/**
* Anax class for wrapping sessions.
*
*/
class Exception extends \Exception
{
/**
* Construct.
*
* @param string $message the Exception message to throw.
* @param int $code the Exception code.
* @param Exception previous the previous exception used for the exception chaining.
*/
public function __construct($message = "", $code = 0, $previous = null)
{
parent::__construct($message, $code, $previous);
}
}

11
src/defines.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
// Version of cimage and img.php
define("CIMAGE_VERSION", "v0.7.19* (2016-08-11)");
// For CRemoteImage
define("CIMAGE_USER_AGENT", "CImage/" . CIMAGE_VERSION);
// Image type IMG_WEBP is only defined from 5.6.25
if (!defined("IMG_WEBP")) {
define("IMG_WEBP", -1);
}

170
src/functions.php Normal file
View File

@@ -0,0 +1,170 @@
<?php
/**
* General functions to use in img.php.
*/
/**
* Trace and log execution to logfile, useful for debugging and development.
*
* @param string $msg message to log to file.
*
* @return void
*/
function trace($msg)
{
$file = CIMAGE_DEBUG_FILE;
if (!is_writable($file)) {
return;
}
$timer = number_format((microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"]), 6);
$details = "{$timer}ms";
$details .= ":" . round(memory_get_peak_usage()/1024/1024, 3) . "MB";
$details .= ":" . count(get_included_files());
file_put_contents($file, "$details:$msg\n", FILE_APPEND);
}
/**
* Display error message.
*
* @param string $msg to display.
* @param int $type of HTTP error to display.
*
* @return void
*/
function errorPage($msg, $type = 500)
{
global $mode;
switch ($type) {
case 403:
$header = "403 Forbidden";
break;
case 404:
$header = "404 Not Found";
break;
default:
$header = "500 Internal Server Error";
}
if ($mode == "strict") {
$header = "404 Not Found";
}
header("HTTP/1.0 $header");
if ($mode == "development") {
die("[img.php] $msg");
}
error_log("[img.php] $msg");
die("HTTP/1.0 $header");
}
/**
* Get input from query string or return default value if not set.
*
* @param mixed $key as string or array of string values to look for in $_GET.
* @param mixed $default value to return when $key is not set in $_GET.
*
* @return mixed value from $_GET or default value.
*/
function get($key, $default = null)
{
if (is_array($key)) {
foreach ($key as $val) {
if (isset($_GET[$val])) {
return $_GET[$val];
}
}
} elseif (isset($_GET[$key])) {
return $_GET[$key];
}
return $default;
}
/**
* Get input from query string and set to $defined if defined or else $undefined.
*
* @param mixed $key as string or array of string values to look for in $_GET.
* @param mixed $defined value to return when $key is set in $_GET.
* @param mixed $undefined value to return when $key is not set in $_GET.
*
* @return mixed value as $defined or $undefined.
*/
function getDefined($key, $defined, $undefined)
{
return get($key) === null ? $undefined : $defined;
}
/**
* Get value from config array or default if key is not set in config array.
*
* @param string $key the key in the config array.
* @param mixed $default value to be default if $key is not set in config.
*
* @return mixed value as $config[$key] or $default.
*/
function getConfig($key, $default)
{
global $config;
return isset($config[$key])
? $config[$key]
: $default;
}
/**
* Log when verbose mode, when used without argument it returns the result.
*
* @param string $msg to log.
*
* @return void or array.
*/
function verbose($msg = null)
{
global $verbose, $verboseFile;
static $log = array();
if (!($verbose || $verboseFile)) {
return;
}
if (is_null($msg)) {
return $log;
}
$log[] = $msg;
}
/**
* Log when verbose mode, when used without argument it returns the result.
*
* @param string $msg to log.
*
* @return void or array.
*/
function checkExternalCommand($what, $enabled, $commandString)
{
$no = $enabled ? null : 'NOT';
$text = "Post processing $what is $no enabled.<br>";
list($command) = explode(" ", $commandString);
$no = is_executable($command) ? null : 'NOT';
$text .= "The command for $what is $no an executable.<br>";
return $text;
}

71
test/CCacheTest.php Normal file
View File

@@ -0,0 +1,71 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
*/
class CCacheTest extends \PHPUnit_Framework_TestCase
{
/**
* Test
*
* @return void
*/
public function testSetCacheDir()
{
$cache = new CCache();
$cache->setDir(CACHE_PATH);
$exp = "exists, writable";
$res = $cache->getStatusOfSubdir("");
$this->assertEquals($exp, $res, "Status of cache dir missmatch.");
}
/**
* Test
*
* @expectedException Exception
*
* @return void
*/
public function testSetWrongCacheDir()
{
$cache = new CCache();
$cache->setDir(CACHE_PATH . "/NO_EXISTS");
}
/**
* Test
*
* @return void
*/
public function testCreateSubdir()
{
$cache = new CCache();
$cache->setDir(CACHE_PATH);
$subdir = "__test__";
$cache->removeSubdir($subdir);
$exp = "does not exist";
$res = $cache->getStatusOfSubdir($subdir, false);
$this->assertEquals($exp, $res, "Subdir should not be created.");
$res = $cache->getPathToSubdir($subdir);
$exp = realpath(CACHE_PATH . "/$subdir");
$this->assertEquals($exp, $res, "Subdir path missmatch.");
$exp = "exists, writable";
$res = $cache->getStatusOfSubdir($subdir);
$this->assertEquals($exp, $res, "Subdir should exist.");
$res = $cache->removeSubdir($subdir);
$this->assertTrue($res, "Remove subdir.");
}
}

View File

@@ -1,10 +1,64 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
*/
class CImageDummyTest extends \PHPUnit_Framework_TestCase
{
const DUMMY = "__dummy__";
private $cachepath;
/**
* Setup environment
*
* @return void
*/
protected function setUp()
{
$cache = new CCache();
$cache->setDir(CACHE_PATH);
$this->cachepath = $cache->getPathToSubdir(self::DUMMY);
}
/**
* Clean up cache dir content.
*
* @return void
*/
protected function removeFilesInCacheDir()
{
$files = glob($this->cachepath . "/*");
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
}
}
}
/**
* Teardown environment
*
* @return void
*/
protected function tearDown()
{
$cache = new CCache();
$cache->setDir(CACHE_PATH);
$this->removeFilesInCacheDir();
$cache->removeSubdir(self::DUMMY);
}
/**
* Test
*
@@ -14,15 +68,15 @@ class CImageDummyTest extends \PHPUnit_Framework_TestCase
{
$img = new CImage();
$img->setSaveFolder(CACHE_PATH . "/dummy");
$img->setSource('dummy', CACHE_PATH . "/dummy");
$img->setSaveFolder($this->cachepath);
$img->setSource(self::DUMMY, $this->cachepath);
$img->createDummyImage();
$img->generateFilename(null, false);
$img->save(null, null, false);
$filename = $img->getTarget();
$this->assertEquals(basename($filename), "dummy_100_100", "Filename not as expected on dummy image.");
$this->assertEquals(basename($filename), self::DUMMY . "_100_100", "Filename not as expected on dummy image.");
}
@@ -36,14 +90,14 @@ class CImageDummyTest extends \PHPUnit_Framework_TestCase
{
$img = new CImage();
$img->setSaveFolder(CACHE_PATH . "/dummy");
$img->setSource('dummy', CACHE_PATH . "/dummy");
$img->setSaveFolder($this->cachepath);
$img->setSource(self::DUMMY, $this->cachepath);
$img->createDummyImage(200, 400);
$img->generateFilename(null, false);
$img->save(null, null, false);
$filename = $img->getTarget();
$this->assertEquals(basename($filename), "dummy_200_400", "Filename not as expected on dummy image.");
$this->assertEquals(basename($filename), self::DUMMY . "_200_400", "Filename not as expected on dummy image.");
}
}

View File

@@ -1,4 +1,7 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
@@ -61,7 +64,7 @@ class CImageRemoteDownloadTest extends \PHPUnit_Framework_TestCase
public function testAllowRemoteDownloadDefaultPatternValid($source)
{
$img = new CImage();
$img->setRemoteDownload(true);
$img->setRemoteDownload(true, "");
$res = $img->isRemoteSource($source);
$this->assertTrue($res, "Should be a valid remote source: '$source'.");
@@ -79,7 +82,7 @@ class CImageRemoteDownloadTest extends \PHPUnit_Framework_TestCase
public function testAllowRemoteDownloadDefaultPatternInvalid($source)
{
$img = new CImage();
$img->setRemoteDownload(true);
$img->setRemoteDownload(true, "");
$res = $img->isRemoteSource($source);
$this->assertFalse($res, "Should not be a valid remote source: '$source'.");

View File

@@ -0,0 +1,74 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
*/
class CImageResizerByAspectRatioTest extends \PHPUnit_Framework_TestCase
{
/**
* Provider
*
* @return array
*/
public function providerImages()
{
return array(
// No value set
array(null, 100, 100, null, null, null, null),
// Aspect ratio 1
array(1, 100, 100, null, null, 100, 100),
array(1, 100, 100, null, 100, 100, 100),
array(1, 100, 100, 100, null, 100, 100),
array(1, 100, 100, 100, 100, 100, 100),
// Aspect ratio 2
array(2, 100, 100, null, null, 100, 50),
array(2, 100, 100, null, 100, 200, 100),
array(2, 100, 100, 100, null, 100, 50),
array(2, 100, 100, 100, 100, 100, 50),
// Aspect ratio 0.5
array(1/2, 100, 100, null, null, 50, 100),
array(1/2, 100, 100, null, 100, 50, 100),
array(1/2, 100, 100, 100, null, 100, 200),
array(1/2, 100, 100, 100, 100, 50, 100),
);
}
/**
* Test
*
* @dataProvider providerImages
*
* @return void
*/
public function testResize1(
$aspectRatio,
$srcWidth,
$srcHeight,
$targetWidth,
$targetHeight,
$expectedWidth,
$expectedHeight
) {
$img = new CImageResizer(/*'logger'*/);
//$img = new CImageResizer('logger');
$img->setSource($srcWidth, $srcHeight)
->setBaseWidthHeight($targetWidth, $targetHeight)
->setBaseAspecRatio($aspectRatio)
->prepareTargetDimensions();
// ->calculateTargetWidthAndHeight();
$this->assertEquals($expectedWidth, $img->getTargetWidth(), "Width not correct.");
$this->assertEquals($expectedHeight, $img->getTargetHeight(), "Height not correct.");
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
*/
class CImageResizerByDevicePixelRatioTest extends \PHPUnit_Framework_TestCase
{
/**
* Provider
*
* @return array
*/
public function providerImages()
{
return array(
// No value set
array(null, 100, 100, null, null, null, null),
// dpr 1
array(1, 100, 100, null, null, 100, 100),
array(1, 100, 100, null, 100, null, 100),
array(1, 100, 100, 100, null, 100, null),
array(1, 100, 100, 100, 100, 100, 100),
// dpr 2
array(2, 100, 100, null, null, 200, 200),
array(2, 100, 100, null, 200, null, 400),
array(2, 100, 100, 200, null, 400, null),
array(2, 100, 100, 200, 200, 400, 400),
// dpr 1/2
array(1/2, 100, 100, null, null, 50, 50),
array(1/2, 100, 100, null, 200, null, 100),
array(1/2, 100, 100, 200, null, 100, null),
array(1/2, 100, 100, 200, 200, 100, 100),
);
}
/**
* Test
*
* @dataProvider providerImages
*
* @return void
*/
public function testResize1(
$dpr,
$srcWidth,
$srcHeight,
$targetWidth,
$targetHeight,
$expectedWidth,
$expectedHeight
) {
$img = new CImageResizer(/*'logger'*/);
//$img = new CImageResizer('logger');
$img->setSource($srcWidth, $srcHeight)
//->setResizeStrategy($img::KEEP_RATIO)
->setBaseWidthHeight($targetWidth, $targetHeight)
->setBaseDevicePixelRate($dpr)
->prepareTargetDimensions();
//->calculateTargetWidthAndHeight();
$this->assertEquals($expectedWidth, $img->getTargetWidth(), "Width not correct.");
$this->assertEquals($expectedHeight, $img->getTargetHeight(), "Height not correct.");
}
}

View File

@@ -0,0 +1,131 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
*/
class CImageResizerStrategyCropToFitTest extends \PHPUnit_Framework_TestCase
{
/**
* Provider
*
* @return array
*/
public function providerImages()
{
return [
// Square
[100, 100, 200, 200, 0, 0, 100, 100],
[100, 100, 200, 100, 0, 25, 100, 50],
[100, 100, 100, 200, 25, 0, 50, 100],
// Landscape
[200, 100, 400, 200, 0, 0, 200, 100],
[200, 100, 50, 50, 50, 0, 100, 100],
[200, 100, 400, 100, 0, 25, 200, 50],
[200, 100, 100, 400, round(175/2), 0, 25, 100],
// Portrait
[100, 200, 50, 100, 0, 0, 100, 200],
[100, 200, 50, 50, 0, 50, 100, 100],
[100, 200, 200, 50, 0, round(175/2), 100, 25],
[100, 200, 50, 200, 25, 0, 50, 200],
];
}
/**
* Test
*
* @dataProvider providerImages
*
* @return void
*/
public function testResize1($sw, $sh, $tw, $th, $cx, $cy, $cw, $ch)
{
$img = new CImageResizer(/*'logger'/**/);
$img->setSource($sw, $sh)
->setBaseWidthHeight($tw, $th)
->setResizeStrategy(CImageResizer::CROP_TO_FIT)
->calculateTargetWidthAndHeight();
$this->assertEquals($tw, $img->getTargetWidth(), "Target width not correct.");
$this->assertEquals($th, $img->getTargetHeight(), "Target height not correct.");
$this->assertEquals($cx, $img->getCropX(), "CropX not correct.");
$this->assertEquals($cy, $img->getCropY(), "CropY not correct.");
$this->assertEquals($cw, $img->getCropWidth(), "CropWidth not correct.");
$this->assertEquals($ch, $img->getCropHeight(), "CropHeight not correct.");
}
/**
* Provider
*
* @return array
*/
public function providerImages2()
{
return [
// Square
[100,100, 200,200, 0,0,100,100, 50,50,100,100],
[100,100, 200,100, 0,0,100,100, 50,0,100,100],
[100,100, 100,200, 0,0,100,100, 0,50,100,100],
// Landscape
[200,100, 400,200, 0,0,200,100, 100,50,200,100],
//[200,100, 50,50, 50,0,100,100, 0,0,200,100],
/*
[200, 100, 400, 100, 0, 25, 200, 50],
[200, 100, 100, 400, round(175/2), 0, 25, 100],
// Portrait
[100, 200, 50, 100, 0, 0, 100, 200],
[100, 200, 50, 50, 0, 50, 100, 100],
[100, 200, 200, 50, 0, round(175/2), 100, 25],
[100, 200, 50, 200, 25, 0, 50, 200],
/* */
];
}
/**
* Test
*
* @dataProvider providerImages2
*
* @return void
*/
public function testResize2($sw, $sh, $tw, $th, $cx, $cy, $cw, $ch, $dx, $dy, $dw, $dh)
{
$img = new CImageResizer(/*'logger'/**/);
$img->setSource($sw, $sh)
->setBaseWidthHeight($tw, $th)
->setResizeStrategy(CImageResizer::CROP_TO_FIT)
->allowUpscale(false)
->calculateTargetWidthAndHeight();
$this->assertEquals($tw, $img->getTargetWidth(), "Target width not correct.");
$this->assertEquals($th, $img->getTargetHeight(), "Target height not correct.");
$this->assertEquals($cx, $img->getCropX(), "CropX not correct.");
$this->assertEquals($cy, $img->getCropY(), "CropY not correct.");
$this->assertEquals($cw, $img->getCropWidth(), "CropWidth not correct.");
$this->assertEquals($ch, $img->getCropHeight(), "CropHeight not correct.");
$this->assertEquals($dx, $img->getDestinationX(), "DestinationX not correct.");
$this->assertEquals($dy, $img->getDestinationY(), "DestinationY not correct.");
$this->assertEquals($dw, $img->getDestinationWidth(), "DestinationWidth not correct.");
$this->assertEquals($dh, $img->getDestinationHeight(), "DestinationHeight not correct.");
}
}

View File

@@ -0,0 +1,119 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
*/
class CImageResizerStrategyFillToFitTest extends \PHPUnit_Framework_TestCase
{
/**
* Provider
*
* @return array
*/
public function providerImages()
{
return array(
// Square
array(100, 100, 200, 200, 0, 0, 200, 200),
array(100, 100, 100, 50, 25, 0, 50, 50),
array(100, 100, 50, 100, 0, 25, 50, 50),
// Landscape
array(200, 100, 400, 200, 0, 0, 400, 200),
array(200, 100, 100, 100, 0, 25, 100, 50),
array(200, 100, 400, 100, 100, 0, 200, 100),
array(200, 100, 100, 400, 0, 175, 100, 50),
// Portrait
array(100, 200, 200, 400, 0, 0, 200, 400),
array(100, 200, 100, 100, 25, 0, 50, 100),
array(100, 200, 400, 100, 175, 0, 50, 100),
array(100, 200, 100, 400, 0, 100, 100, 200),
);
}
/**
* Test
*
* @dataProvider providerImages
*
* @return void
*/
public function testResize1($sw, $sh, $tw, $th, $dx, $dy, $dw, $dh)
{
$img = new CImageResizer(/*'logger'/**/);
$img->setSource($sw, $sh)
->setBaseWidthHeight($tw, $th)
->setResizeStrategy(CImageResizer::FILL_TO_FIT)
->calculateTargetWidthAndHeight();
$this->assertEquals($tw, $img->getTargetWidth(), "Target width not correct.");
$this->assertEquals($th, $img->getTargetHeight(), "Target height not correct.");
$this->assertEquals($dx, $img->getDestinationX(), "DestinationX not correct.");
$this->assertEquals($dy, $img->getDestinationY(), "DestinationY not correct.");
$this->assertEquals($dw, $img->getDestinationWidth(), "DestinationWidth not correct.");
$this->assertEquals($dh, $img->getDestinationHeight(), "DestinationHeight not correct.");
}
/**
* Provider
*
* @return array
*/
public function providerImages2()
{
return [
// Square
[100, 100, 200, 200, 50, 50, 100, 100],
[100, 100, 400, 100, 150, 0, 100, 100],
[100, 100, 100, 400, 0, 150, 100, 100],
[100, 100, 400, 400, 150, 150, 100, 100],
[491, 323, 600, 400, 55, 39, 491, 323],
// Landscape
// Portrait
];
}
/**
* Test
*
* @dataProvider providerImages2
*
* @return void
*/
public function testResize2($sw, $sh, $tw, $th, $dx, $dy, $dw, $dh)
{
$img = new CImageResizer(/*'logger'/**/);
$img->setSource($sw, $sh)
->setBaseWidthHeight($tw, $th)
->setResizeStrategy(CImageResizer::FILL_TO_FIT)
->allowUpscale(false)
->calculateTargetWidthAndHeight();
$this->assertEquals($tw, $img->getTargetWidth(), "Target width not correct.");
$this->assertEquals($th, $img->getTargetHeight(), "Target height not correct.");
$this->assertEquals($dx, $img->getDestinationX(), "DestinationX not correct.");
$this->assertEquals($dy, $img->getDestinationY(), "DestinationY not correct.");
$this->assertEquals($dw, $img->getDestinationWidth(), "DestinationWidth not correct.");
$this->assertEquals($dh, $img->getDestinationHeight(), "DestinationHeight not correct.");
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
*/
class CImageResizerStrategyKeepAspectRatioTest extends \PHPUnit_Framework_TestCase
{
/**
* Provider
*
* @return array
*/
public function providerImages()
{
return array(
// Square
array(100, 100, null, null, 100, 100, 0, 0, 100, 100),
array(100, 100, null, 200, 200, 200, 0, 0, 100, 100),
array(100, 100, 200, null, 200, 200, 0, 0, 100, 100),
array(100, 100, 200, 200, 200, 200, 0, 0, 100, 100),
// Landscape
array(200, 100, null, null, 200, 100, 0, 0, 200, 100),
array(200, 100, null, 200, 400, 200, 0, 0, 200, 100),
array(200, 100, 400, null, 400, 200, 0, 0, 200, 100),
array(200, 100, 400, 200, 400, 200, 0, 0, 200, 100),
// Portrait
array(100, 200, null, null, 100, 200, 0, 0, 100, 200),
array(100, 200, null, 100, 50, 100, 0, 0, 100, 200),
array(100, 200, 50, null, 50, 100, 0, 0, 100, 200),
array(100, 200, 50, 100, 50, 100, 0, 0, 100, 200),
);
}
/**
* Test
*
* @dataProvider providerImages
*
* @return void
*/
public function testResize1($sw, $sh, $tw, $th, $twa, $tha, $cx, $cy, $cw, $ch)
{
$img = new CImageResizer(/*'logger'/**/);
$img->setSource($sw, $sh)
->setBaseWidthHeight($tw, $th)
->setResizeStrategy(CImageResizer::KEEP_RATIO)
->calculateTargetWidthAndHeight();
$this->assertEquals($twa, $img->getTargetWidth(), "Target width not correct.");
$this->assertEquals($tha, $img->getTargetHeight(), "Target height not correct.");
$this->assertEquals($cx, $img->getCropX(), "CropX not correct.");
$this->assertEquals($cy, $img->getCropY(), "CropY not correct.");
$this->assertEquals($cw, $img->getCropWidth(), "CropWidth not correct.");
$this->assertEquals($ch, $img->getCropHeight(), "CropHeight not correct.");
}
/**
* Provider
*
* @return array
*/
public function providerImages2()
{
return array(
// Square
array(100, 100, 100, 100, 100, 100, 0, 0, 100, 100),
array(100, 100, null, 200, 100, 100, 0, 0, 100, 100),
array(100, 100, 200, null, 100, 100, 0, 0, 100, 100),
array(100, 100, 200, 100, 100, 100, 0, 0, 100, 100),
array(100, 100, 100, 200, 100, 100, 0, 0, 100, 100),
array(100, 100, 200, 200, 100, 100, 0, 0, 100, 100),
// Landscape
//array(200, 100, null, null, 200, 100, 0, 0, 200, 100),
//array(200, 100, null, 200, 400, 200, 0, 0, 200, 100),
//array(200, 100, 400, null, 400, 200, 0, 0, 200, 100),
//array(200, 100, 400, 200, 400, 200, 0, 0, 200, 100),
// Portrait
//array(100, 200, null, null, 100, 200, 0, 0, 100, 200),
//array(100, 200, null, 100, 50, 100, 0, 0, 100, 200),
//array(100, 200, 50, null, 50, 100, 0, 0, 100, 200),
//array(100, 200, 50, 100, 50, 100, 0, 0, 100, 200),
);
}
/**
* Test
*
* @dataProvider providerImages2
*
* @return void
*/
public function testResize2($sw, $sh, $tw, $th, $twa, $tha, $cx, $cy, $cw, $ch)
{
$img = new CImageResizer(/*'logger'/**/);
$img->setSource($sw, $sh)
->setBaseWidthHeight($tw, $th)
->setResizeStrategy(CImageResizer::KEEP_RATIO)
->allowUpscale(false)
->calculateTargetWidthAndHeight();
$this->assertEquals($twa, $img->getTargetWidth(), "Target width not correct.");
$this->assertEquals($tha, $img->getTargetHeight(), "Target height not correct.");
$this->assertEquals($cx, $img->getCropX(), "CropX not correct.");
$this->assertEquals($cy, $img->getCropY(), "CropY not correct.");
$this->assertEquals($cw, $img->getCropWidth(), "CropWidth not correct.");
$this->assertEquals($ch, $img->getCropHeight(), "CropHeight not correct.");
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
*/
class CImageResizerStrategyStretchTest extends \PHPUnit_Framework_TestCase
{
/**
* Provider
*
* @return array
*/
public function providerImages()
{
return array(
// Square
array(100, 100, 200, 200),
array(100, 100, 200, 100),
array(100, 100, 100, 200),
// Landscape
array(200, 100, 400, 200),
array(200, 100, 100, 200),
// Portrait
array(100, 200, 50, 100),
array(100, 200, 100, 100),
);
}
/**
* Test
*
* @dataProvider providerImages
*
* @return void
*/
public function testResize1($sw, $sh, $tw, $th)
{
$img = new CImageResizer(/*'logger'/**/);
$img->setSource($sw, $sh)
->setBaseWidthHeight($tw, $th)
->setResizeStrategy(CImageResizer::STRETCH)
->calculateTargetWidthAndHeight();
$this->assertEquals($tw, $img->getTargetWidth(), "Target width not correct.");
$this->assertEquals($th, $img->getTargetHeight(), "Target height not correct.");
}
}

207
test/CImageResizerTest.php Normal file
View File

@@ -0,0 +1,207 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
*/
function logger($str)
{
echo "$str\n";
}
function loggerDummy($str)
{
;
}
class CImageResizerTest extends \PHPUnit_Framework_TestCase
{
/**
* Provider
*
* @return array
*/
public function providerFaultImages()
{
return array(
array('xx', 100, null, 1),
array( 100, 'yy', null, 1),
array( 100, 100, 'zz', 1),
array( 100, 100, null, -1),
);
}
/**
* Test
*
* @dataProvider providerFaultImages
*
* @expectedException Exception
*
* @return void
*/
public function testResizeFaults($targetWidth, $targetHeight, $aspectRatio, $dpr)
{
$img = new CImageResizer(/*'logger'*/);
$img->setBaseWidthHeight($targetWidth, $targetHeight)
->setBaseAspecRatio($aspectRatio)
->setBaseDevicePixelRate($dpr);
}
/**
* Test
*
* @return void
*/
public function testLogger()
{
$img = new CImageResizer('Mos\CImage\loggerDummy');
$img->setBaseWidthHeight(100, 100);
}
/**
* Provider
*
* @return array
*/
public function providerResizeStrategy()
{
return array(
array(CImageResizer::KEEP_RATIO, "KEEP_RATIO"),
array(CImageResizer::CROP_TO_FIT, "CROP_TO_FIT"),
array(CImageResizer::FILL_TO_FIT, "FILL_TO_FIT"),
array(CImageResizer::STRETCH, "STRETCH"),
array(-1, "UNKNOWN"),
);
}
/**
* Test
*
* @dataProvider providerResizeStrategy
*
* @return void
*/
public function testResizeStrategy($strategy, $str)
{
$img = new CImageResizer(/*'logger'*/);
$img->setResizeStrategy($strategy);
$res = $img->getResizeStrategyAsString();
$this->assertEquals($str, $res, "Strategy not matching.");
}
/**
* Provider
*
* @return array
*/
public function providerPercent()
{
return array(
array(100, 100, "100%", "100%", 100, 100),
array(100, 100, "50%", "50%", 50, 50),
);
}
/**
* Test
*
* @dataProvider providerPercent
*
* @return void
*/
public function testPercent($sw, $sh, $tw, $th, $w, $h)
{
$img = new CImageResizer(/*'logger'*/);
$img->setSource($sw, $sh)
->setBaseWidthHeight($tw, $th);
$this->assertEquals($w, $img->getTargetWidth(), "Target width not correct.");
$this->assertEquals($h, $img->getTargetHeight(), "Target height not correct.");
}
/**
* Test
*
* @return void
*/
public function testGetSource()
{
$img = new CImageResizer(/*'logger'*/);
$w = 100;
$h = 100;
$img->setSource($w, $h);
$this->assertEquals($w, $img->getSourceWidth(), "Source width not correct.");
$this->assertEquals($h, $img->getSourceHeight(), "Source height not correct.");
}
/**
* Provider
*
* @return array
*/
public function providerImages()
{
return [
// car.png
[CImageResizer::KEEP_RATIO, 491, 324, 500, 200, 303, 200, 0, 0, 491, 324],
[CImageResizer::KEEP_RATIO, 491, 324, 500, 500, 500, 330, 0, 0, 491, 324],
];
}
/**
* Test
*
* @dataProvider providerImages
*
* @return void
*/
public function testResize($strat, $sw, $sh, $tw, $th, $twa, $tha, $cx, $cy, $cw, $ch)
{
$img = new CImageResizer(/*'logger'/**/);
$img->setSource($sw, $sh)
->setBaseWidthHeight($tw, $th)
->setResizeStrategy($strat)
->calculateTargetWidthAndHeight();
$this->assertEquals($twa, $img->getTargetWidth(), "Target width not correct.");
$this->assertEquals($tha, $img->getTargetHeight(), "Target height not correct.");
$this->assertEquals($cx, $img->getCropX(), "CropX not correct.");
$this->assertEquals($cy, $img->getCropY(), "CropY not correct.");
$this->assertEquals($cw, $img->getCropWidth(), "CropWidth not correct.");
$this->assertEquals($ch, $img->getCropHeight(), "CropHeight not correct.");
}
}

View File

@@ -1,4 +1,7 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*
@@ -38,7 +41,7 @@ class CImageSRGBTest extends \PHPUnit_Framework_TestCase
$img = new CImage();
$filename = $img->convert2sRGBColorSpace(
'car.png',
'car.png',
IMAGE_PATH,
$this->cache,
$this->srgbColorProfile
@@ -63,8 +66,8 @@ class CImageSRGBTest extends \PHPUnit_Framework_TestCase
$img = new CImage();
$filename = $img->convert2sRGBColorSpace(
'car.jpg',
IMAGE_PATH,
'car.jpg',
IMAGE_PATH,
$this->cache,
$this->srgbColorProfile
);

54
test/CImgTest.php Normal file
View File

@@ -0,0 +1,54 @@
<?php
namespace Mos\CImage;
/**
* A testclass for img.php
*
*/
class CImgTest extends \PHPUnit_Framework_TestCase
{
/**
* Provider
*
* @return array
*/
public function providerQueryString()
{
return [
//
[[
"src" => "car.png",
"json" => true,
"rotate" => 90,
]],
];
}
/**
* Test
*
* @-preserveGlobalState disabled
* @runInSeparateProcess
*
* @dataProvider providerQueryString
*
* @return void
*/
public function testResize($query)
{
//$_GET = $query;
#ob_start();
//$res = require "webroot/img.php";
#$res = ob_get_clean();
//echo "MOPED $res";
}
}

View File

@@ -1,4 +1,7 @@
<?php
namespace Mos\CImage;
/**
* A testclass
*

13
test/Tests.txt Normal file
View File

@@ -0,0 +1,13 @@
# Tests to be done
Use resize fill-to-fit, crop-to-fit, stretch without width and height should result in image having original width and height.
## Integration tests
Checkout a version using composer and execute all tests, prefarably on cimage.se.
## Reference images
Create a bunch of reference images to compare between versions. Save together with how it was created.

View File

@@ -3,7 +3,12 @@
* Get all configuration details to be able to execute the test suite.
*
*/
require __DIR__ . "/../autoload.php";
require __DIR__ . "/../vendor/autoload.php";
define('IMAGE_PATH', __DIR__ . '/../webroot/img/');
define('CACHE_PATH', __DIR__ . '/../cache/');
if (!defined("IMAGE_PATH")) {
define("IMAGE_PATH", __DIR__ . "/../webroot/img/");
}
if (!defined("CACHE_PATH")) {
define("CACHE_PATH", __DIR__ . "/../cache/");
}

7
vendor/autoload.php vendored Normal file
View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit2b4448ad4be850d755525f1f201dede8::getLoader();

441
vendor/composer/ClassLoader.php vendored Normal file
View File

@@ -0,0 +1,441 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

21
vendor/composer/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
Copyright (c) 2016 Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

9
vendor/composer/autoload_classmap.php vendored Normal file
View File

@@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

11
vendor/composer/autoload_files.php vendored Normal file
View File

@@ -0,0 +1,11 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'c033e147fa8801d0fd8ea2bc8fa37e2a' => $baseDir . '/src/defines.php',
'3b345cc3469552d097f31e5a67742144' => $baseDir . '/src/functions.php',
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

10
vendor/composer/autoload_psr4.php vendored Normal file
View File

@@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Mos\\' => array($baseDir . '/src'),
);

70
vendor/composer/autoload_real.php vendored Normal file
View File

@@ -0,0 +1,70 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit2b4448ad4be850d755525f1f201dede8
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit2b4448ad4be850d755525f1f201dede8', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit2b4448ad4be850d755525f1f201dede8', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit2b4448ad4be850d755525f1f201dede8::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit2b4448ad4be850d755525f1f201dede8::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire2b4448ad4be850d755525f1f201dede8($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire2b4448ad4be850d755525f1f201dede8($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

36
vendor/composer/autoload_static.php vendored Normal file
View File

@@ -0,0 +1,36 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit2b4448ad4be850d755525f1f201dede8
{
public static $files = array (
'c033e147fa8801d0fd8ea2bc8fa37e2a' => __DIR__ . '/../..' . '/src/defines.php',
'3b345cc3469552d097f31e5a67742144' => __DIR__ . '/../..' . '/src/functions.php',
);
public static $prefixLengthsPsr4 = array (
'M' =>
array (
'Mos\\' => 4,
),
);
public static $prefixDirsPsr4 = array (
'Mos\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit2b4448ad4be850d755525f1f201dede8::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit2b4448ad4be850d755525f1f201dede8::$prefixDirsPsr4;
}, null, ClassLoader::class);
}
}

1
vendor/composer/installed.json vendored Normal file
View File

@@ -0,0 +1 @@
[]

View File

@@ -2,7 +2,7 @@
echo 'Current PHP version: ' . phpversion() . '<br><br>';
echo 'Running on: ' . $_SERVER['SERVER_SOFTWARE'] . '<br><br>';
echo 'Running on: ' . htmlentities($_SERVER['SERVER_SOFTWARE']) . '<br><br>';
$no = extension_loaded('exif') ? null : 'NOT';
echo "Extension exif is $no loaded.<br>";
@@ -15,13 +15,15 @@ echo "Extension imagick is $no loaded.<br>";
$no = extension_loaded('gd') ? null : 'NOT';
echo "Extension gd is $no loaded.<br>";
if (!$no) {
echo "<pre>", var_dump(gd_info()), "</pre>";
}
echo "<strong>Checking path for postprocessing tools</strong>";
echo "<br>pngquant: ";
system("which pngquant");
echo "<br>optipng: ";
system("which optipng");

View File

@@ -4,7 +4,7 @@
# The example is set up as following.
#
# img A directory where all images are stored
# img/me.jpg Access a image as usually.
# img/me.jpg Access a image as usual.
# image/me.jpg Access a image though img.php using htaccess rewrite.
# image/me.jpg?w=300 Using options to img.php.
#

View File

@@ -8,45 +8,6 @@
*
*/
$version = "v0.7.8 (2015-12-06)";
/**
* Display error message.
*
* @param string $msg to display.
* @param int $type of HTTP error to display.
*
* @return void
*/
function errorPage($msg, $type = 500)
{
global $mode;
switch ($type) {
case 403:
$header = "403 Forbidden";
break;
case 404:
$header = "404 Not Found";
break;
default:
$header = "500 Internal Server Error";
}
header("HTTP/1.0 $header");
if ($mode == 'development') {
die("[img.php] $msg");
}
error_log("[img.php] $msg");
die("HTTP/1.0 $header");
}
/**
* Custom exception handler.
*/
@@ -56,95 +17,13 @@ set_exception_handler(function ($exception) {
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>"
, 500);
. "</pre>",
500
);
});
/**
* Get input from query string or return default value if not set.
*
* @param mixed $key as string or array of string values to look for in $_GET.
* @param mixed $default value to return when $key is not set in $_GET.
*
* @return mixed value from $_GET or default value.
*/
function get($key, $default = null)
{
if (is_array($key)) {
foreach ($key as $val) {
if (isset($_GET[$val])) {
return $_GET[$val];
}
}
} elseif (isset($_GET[$key])) {
return $_GET[$key];
}
return $default;
}
/**
* Get input from query string and set to $defined if defined or else $undefined.
*
* @param mixed $key as string or array of string values to look for in $_GET.
* @param mixed $defined value to return when $key is set in $_GET.
* @param mixed $undefined value to return when $key is not set in $_GET.
*
* @return mixed value as $defined or $undefined.
*/
function getDefined($key, $defined, $undefined)
{
return get($key) === null ? $undefined : $defined;
}
/**
* Get value from config array or default if key is not set in config array.
*
* @param string $key the key in the config array.
* @param mixed $default value to be default if $key is not set in config.
*
* @return mixed value as $config[$key] or $default.
*/
function getConfig($key, $default)
{
global $config;
return isset($config[$key])
? $config[$key]
: $default;
}
/**
* Log when verbose mode, when used without argument it returns the result.
*
* @param string $msg to log.
*
* @return void or array.
*/
function verbose($msg = null)
{
global $verbose, $verboseFile;
static $log = array();
if (!($verbose || $verboseFile)) {
return;
}
if (is_null($msg)) {
return $log;
}
$log[] = $msg;
}
/**
* Get configuration options from file, if the file exists, else use $config
* if its defined or create an empty $config.
@@ -157,6 +36,24 @@ if (is_file($configFile)) {
$config = array();
}
// Make CIMAGE_DEBUG false by default, if not already defined
if (!defined("CIMAGE_DEBUG")) {
define("CIMAGE_DEBUG", false);
}
/**
* Setup the autoloader, but not when using a bundle.
*/
if (!defined("CIMAGE_BUNDLE")) {
if (!isset($config["autoloader"])) {
die("CImage: Missing autoloader.");
}
require $config["autoloader"];
}
/**
@@ -165,7 +62,7 @@ if (is_file($configFile)) {
*/
$verbose = getDefined(array('verbose', 'v'), true, false);
$verboseFile = getDefined('vf', true, false);
verbose("img.php version = $version");
verbose("img.php version = " . CIMAGE_VERSION);
@@ -192,36 +89,28 @@ if (!extension_loaded('gd')) {
// Specific settings for each mode
if ($mode == 'strict') {
error_reporting(0);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
$verbose = false;
$status = false;
$verboseFile = false;
} elseif ($mode == 'production') {
error_reporting(-1);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
$verbose = false;
$status = false;
$verboseFile = false;
} elseif ($mode == 'development') {
error_reporting(-1);
ini_set('display_errors', 1);
ini_set('log_errors', 0);
$verboseFile = false;
} elseif ($mode == 'test') {
error_reporting(-1);
ini_set('display_errors', 1);
ini_set('log_errors', 0);
} else {
errorPage("Unknown mode: $mode", 500);
}
@@ -256,7 +145,7 @@ $pwd = get(array('password', 'pwd'), null);
// Check if passwords match, if configured to use passwords
$passwordMatch = null;
if ($pwd) {
switch($pwdType) {
switch ($pwdType) {
case 'md5':
$passwordMatch = ($pwdConfig === md5($pwd));
break;
@@ -311,7 +200,6 @@ if (!$allowHotlinking) {
} else {
errorPage("Hotlinking/leeching not allowed by whitelist. Referer: $referer.", 403);
}
} else {
errorPage("Hotlinking/leeching not allowed.", 403);
}
@@ -324,24 +212,58 @@ verbose("referer host = $refererHost");
/**
* Get the source files.
* Create the class for the image.
*/
$autoloader = getConfig('autoloader', false);
$cimageClass = getConfig('cimage_class', false);
if ($autoloader) {
require $autoloader;
} elseif ($cimageClass) {
require $cimageClass;
}
$CImage = getConfig('CImage', '\Mos\CImage\CImage');
$img = new $CImage();
$img->setVerbose($verbose || $verboseFile);
/**
* Create the class for the image.
* Get the cachepath from config.
*/
$img = new CImage();
$img->setVerbose($verbose || $verboseFile);
$CCache = getConfig('CCache', '\Mos\CImage\CCache');
$cachePath = getConfig('cache_path', __DIR__ . '/../cache/');
$cache = new $CCache();
$cache->setDir($cachePath);
/**
* no-cache, nc - skip the cached version and process and create a new version in cache.
*/
$useCache = getDefined(array('no-cache', 'nc'), false, true);
verbose("use cache = $useCache");
/**
* Prepare fast track cache for swriting cache items.
*/
$fastTrackCache = "fasttrack";
$allowFastTrackCache = getConfig('fast_track_allow', false);
$CFastTrackCache = getConfig('CFastTrackCache', '\Mos\CImage\CFastTrackCache');
$ftc = new $CFastTrackCache();
$ftc->setCacheDir($cache->getPathToSubdir($fastTrackCache))
->enable($allowFastTrackCache)
->setFilename(array('no-cache', 'nc'));
$img->injectDependency("fastTrackCache", $ftc);
/**
* Load and output images from fast track cache, if items are available
* in cache.
*/
if ($useCache && $allowFastTrackCache) {
if (CIMAGE_DEBUG) {
trace("img.php fast track cache enabled and used");
}
$ftc->output();
}
@@ -353,8 +275,10 @@ $img->setVerbose($verbose || $verboseFile);
$allowRemote = getConfig('remote_allow', false);
if ($allowRemote && $passwordMatch !== false) {
$cacheRemote = $cache->getPathToSubdir("remote");
$pattern = getConfig('remote_pattern', null);
$img->setRemoteDownload($allowRemote, $pattern);
$img->setRemoteDownload($allowRemote, $cacheRemote, $pattern);
$whitelist = getConfig('remote_whitelist', null);
$img->setRemoteHostWhitelist($whitelist);
@@ -374,8 +298,8 @@ $shortcutConfig = getConfig('shortcut', array(
verbose("shortcut = $shortcut");
if (isset($shortcut)
&& isset($shortcutConfig[$shortcut])) {
&& isset($shortcutConfig[$shortcut])
) {
parse_str($shortcutConfig[$shortcut], $get);
verbose("shortcut-constant = {$shortcutConfig[$shortcut]}");
$_GET = array_merge($_GET, $get);
@@ -389,45 +313,73 @@ if (isset($shortcut)
$srcImage = urldecode(get('src'))
or errorPage('Must set src-attribute.', 404);
// Get settings for src-alt as backup image
$srcAltImage = urldecode(get('src-alt', null));
$srcAltConfig = getConfig('src_alt', null);
if (empty($srcAltImage)) {
$srcAltImage = $srcAltConfig;
}
// Check for valid/invalid characters
$imagePath = getConfig('image_path', __DIR__ . '/img/');
$imagePathConstraint = getConfig('image_path_constraint', true);
$validFilename = getConfig('valid_filename', '#^[a-z0-9A-Z-/_ \.:]+$#');
// Source is remote
$remoteSource = false;
// Dummy image feature
$dummyEnabled = getConfig('dummy_enabled', true);
$dummyFilename = getConfig('dummy_filename', 'dummy');
$dummyImage = false;
preg_match($validFilename, $srcImage)
or errorPage('Filename contains invalid characters.', 404);
or errorPage('Source filename contains invalid characters.', 404);
if ($dummyEnabled && $srcImage === $dummyFilename) {
// Prepare to create a dummy image and use it as the source image.
$dummyImage = true;
} elseif ($allowRemote && $img->isRemoteSource($srcImage)) {
// If source is a remote file, ignore local file checks.
} elseif ($imagePathConstraint) {
// Check that the image is a file below the directory 'image_path'.
$remoteSource = true;
} else {
// Check if file exists on disk or try using src-alt
$pathToImage = realpath($imagePath . $srcImage);
$imageDir = realpath($imagePath);
is_file($pathToImage)
or errorPage(
'Source image is not a valid file, check the filename and that a
matching file exists on the filesystem.'
, 404);
if (!is_file($pathToImage) && !empty($srcAltImage)) {
// Try using the src-alt instead
$srcImage = $srcAltImage;
$pathToImage = realpath($imagePath . $srcImage);
preg_match($validFilename, $srcImage)
or errorPage('Source (alt) filename contains invalid characters.', 404);
if ($dummyEnabled && $srcImage === $dummyFilename) {
// Check if src-alt is the dummy image
$dummyImage = true;
}
}
if (!$dummyImage) {
is_file($pathToImage)
or errorPage(
'Source image is not a valid file, check the filename and that a
matching file exists on the filesystem.',
404
);
}
}
if ($imagePathConstraint && !$dummyImage && !$remoteSource) {
// Check that the image is a file below the directory 'image_path'.
$imageDir = realpath($imagePath);
substr_compare($imageDir, $pathToImage, 0, strlen($imageDir)) == 0
or errorPage(
'Security constraint: Source image is not below the directory "image_path"
as specified in the config file img_config.php.'
, 404);
as specified in the config file img_config.php.',
404
);
}
verbose("src = $srcImage");
@@ -646,23 +598,14 @@ $useOriginal = getDefined(array('skip-original', 'so'), false, true);
$useOriginalDefault = getConfig('skip_original', false);
if ($useOriginalDefault === true) {
verbose("use original is default ON");
$useOriginal = true;
verbose("skip original is default ON");
$useOriginal = false;
}
verbose("use original = $useOriginal");
/**
* no-cache, nc - skip the cached version and process and create a new version in cache.
*/
$useCache = getDefined(array('no-cache', 'nc'), false, true);
verbose("use cache = $useCache");
/**
* quality, q - set level of quality for jpeg images
*/
@@ -899,6 +842,9 @@ verbose("upscale = $upscale");
* Get details for post processing
*/
$postProcessing = getConfig('postprocessing', array(
'png_lossy' => false,
'png_lossy_cmd' => '/usr/local/bin/pngquant --force --output',
'png_filter' => false,
'png_filter_cmd' => '/usr/local/bin/optipng -q',
@@ -911,6 +857,15 @@ $postProcessing = getConfig('postprocessing', array(
/**
* lossy - Do lossy postprocessing, if available.
*/
$lossy = getDefined(array('lossy'), true, null);
verbose("lossy = $lossy");
/**
* alias - Save resulting image to another alias name.
* Password always apply, must be defined.
@@ -940,14 +895,7 @@ verbose("alias = $alias");
/**
* Get the cachepath from config.
*/
$cachePath = getConfig('cache_path', __DIR__ . '/../cache/');
/**
* Get the cachepath from config.
* Add cache control HTTP header.
*/
$cacheControl = getConfig('cache_control', null);
@@ -961,11 +909,8 @@ if ($cacheControl) {
/**
* Prepare a dummy image and use it as source image.
*/
$dummyDir = getConfig('dummy_dir', $cachePath. "/" . $dummyFilename);
if ($dummyImage === true) {
is_writable($dummyDir)
or verbose("dummy dir not writable = $dummyDir");
$dummyDir = $cache->getPathToSubdir("dummy");
$img->setSaveFolder($dummyDir)
->setSource($dummyFilename, $dummyDir)
@@ -993,24 +938,16 @@ if ($dummyImage === true) {
/**
* Prepare a sRGB version of the image and use it as source image.
*/
$srgbDirName = "/srgb";
$srgbDir = realpath(getConfig('srgb_dir', $cachePath . $srgbDirName));
$srgbDefault = getConfig('srgb_default', false);
$srgbColorProfile = getConfig('srgb_colorprofile', __DIR__ . '/../icc/sRGB_IEC61966-2-1_black_scaled.icc');
$srgb = getDefined('srgb', true, null);
if ($srgb || $srgbDefault) {
if (!is_writable($srgbDir)) {
if (is_writable($cachePath)) {
mkdir($srgbDir);
}
}
$filename = $img->convert2sRGBColorSpace(
$srcImage,
$imagePath,
$srgbDir,
$cache->getPathToSubdir("srgb"),
$srgbColorProfile,
$useCache
);
@@ -1030,13 +967,26 @@ if ($srgb || $srgbDefault) {
* Display status
*/
if ($status) {
$text = "img.php version = $version\n";
$text = "img.php version = " . CIMAGE_VERSION . "\n";
$text .= "PHP version = " . PHP_VERSION . "\n";
$text .= "Running on: " . $_SERVER['SERVER_SOFTWARE'] . "\n";
$text .= "Allow remote images = $allowRemote\n";
$text .= "Cache writable = " . is_writable($cachePath) . "\n";
$text .= "Cache dummy writable = " . is_writable($dummyDir) . "\n";
$text .= "Cache srgb writable = " . is_writable($srgbDir) . "\n";
$res = $cache->getStatusOfSubdir("");
$text .= "Cache $res\n";
$res = $cache->getStatusOfSubdir("remote");
$text .= "Cache remote $res\n";
$res = $cache->getStatusOfSubdir("dummy");
$text .= "Cache dummy $res\n";
$res = $cache->getStatusOfSubdir("srgb");
$text .= "Cache srgb $res\n";
$res = $cache->getStatusOfSubdir($fastTrackCache);
$text .= "Cache fasttrack $res\n";
$text .= "Alias path writable = " . is_writable($aliasPath) . "\n";
$no = extension_loaded('exif') ? null : 'NOT';
@@ -1051,6 +1001,11 @@ if ($status) {
$no = extension_loaded('gd') ? null : 'NOT';
$text .= "Extension gd is $no loaded.<br>";
$text .= checkExternalCommand("PNG LOSSY", $postProcessing["png_lossy"], $postProcessing["png_lossy_cmd"]);
$text .= checkExternalCommand("PNG FILTER", $postProcessing["png_filter"], $postProcessing["png_filter_cmd"]);
$text .= checkExternalCommand("PNG DEFLATE", $postProcessing["png_deflate"], $postProcessing["png_deflate_cmd"]);
$text .= checkExternalCommand("JPEG OPTIMIZE", $postProcessing["jpeg_optimize"], $postProcessing["jpeg_optimize_cmd"]);
if (!$no) {
$text .= print_r(gd_info(), 1);
}
@@ -1119,6 +1074,7 @@ if (is_callable($hookBeforeCImage)) {
// Other
'postProcessing' => $postProcessing,
'lossy' => $lossy,
));
verbose(print_r($allConfig, 1));
extract($allConfig);
@@ -1166,7 +1122,7 @@ EOD;
/**
* Load, process and output the image
*/
$img->log("Incoming arguments: " . print_r(verbose(), 1))
$res = $img->log("Incoming arguments: " . print_r(verbose(), 1))
->setSaveFolder($cachePath)
->useCache($useCache)
->setSource($srcImage, $imagePath)
@@ -1203,6 +1159,9 @@ $img->log("Incoming arguments: " . print_r(verbose(), 1))
// Output format
'outputFormat' => $outputFormat,
'dpr' => $dpr,
// Postprocessing using external tools
'lossy' => $lossy,
)
)
->loadImageDetails()
@@ -1222,3 +1181,5 @@ $img->log("Incoming arguments: " . print_r(verbose(), 1))
->save()
->linkToCacheFile($aliasTarget)
->output();
return $res;

BIN
webroot/img/apple.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

171
webroot/img/drawing.svg Normal file
View File

@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 744.09448819 1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="drawing.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2"
inkscape:cx="76.428572"
inkscape:cy="-22.526694"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="919"
inkscape:window-height="416"
inkscape:window-x="167"
inkscape:window-y="177"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-opacity:1"
id="rect4146"
width="100"
height="100"
x="-7.4924429e-08"
y="952.36218"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:#009600;fill-opacity:1;stroke:none;stroke-opacity:1"
d="m 0,952.3622 0,20 20,0 0,-20 -20,0 z"
id="rect4136"
inkscape:connector-curvature="0"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
inkscape:connector-curvature="0"
id="path4140"
d="m 166,948.8622 0,10 10,0 0,-10 -10,0 z"
style="fill:#009600;fill-opacity:1;stroke:none;stroke-opacity:1" />
<path
style="fill:#009600;fill-opacity:1;stroke:none;stroke-opacity:1"
d="m 185.5,949.3622 0,10 10,0 0,-10 -10,0 z"
id="path4142"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path4144"
d="m 201.5,949.8622 0,10 10,0 0,-10 -10,0 z"
style="fill:#009600;fill-opacity:1;stroke:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4148"
d="m 40,952.3622 0,20 20,0 0,-20 -20,0 z"
style="fill:#ffff00;fill-opacity:1;stroke:none;stroke-opacity:1"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-opacity:1"
d="m 80,952.3622 0,20 20,0 0,-20 -20,0 z"
id="path4150"
inkscape:connector-curvature="0"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
inkscape:connector-curvature="0"
id="path4152"
d="m 20,972.3622 0,20 20,0 0,-20 -20,0 z"
style="fill:#00ffff;fill-opacity:1;stroke:none;stroke-opacity:1"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:#000080;fill-opacity:1;stroke:none;stroke-opacity:1"
d="m 60,972.3622 0,20 20,0 0,-20 -20,0 z"
id="path4154"
inkscape:connector-curvature="0"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
inkscape:connector-curvature="0"
id="path4156"
d="m 0,992.3622 0,20 20,0 0,-20 -20,0 z"
style="fill:#009600;fill-opacity:1;stroke:none;stroke-opacity:1"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:#ffff00;fill-opacity:1;stroke:none;stroke-opacity:1"
d="m 40,992.3622 0,20 20,0 0,-20 -20,0 z"
id="path4158"
inkscape:connector-curvature="0"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
inkscape:connector-curvature="0"
id="path4160"
d="m 80,992.3622 0,20 20,0 0,-20 -20,0 z"
style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-opacity:1"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:#00ffff;fill-opacity:1;stroke:none;stroke-opacity:1"
d="m 20,1012.3622 0,20 20,0 0,-20 -20,0 z"
id="path4162"
inkscape:connector-curvature="0"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
inkscape:connector-curvature="0"
id="path4164"
d="m 60,1012.3622 0,20 20,0 0,-20 -20,0 z"
style="fill:#000080;fill-opacity:1;stroke:none;stroke-opacity:1"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
inkscape:connector-curvature="0"
id="path4166"
d="m 0,1032.3622 0,20 20,0 0,-20 -20,0 z"
style="fill:#009600;fill-opacity:1;stroke:none;stroke-opacity:1"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:#ffff00;fill-opacity:1;stroke:none;stroke-opacity:1"
d="m 40,1032.3622 0,20 20,0 0,-20 -20,0 z"
id="path4168"
inkscape:connector-curvature="0"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
inkscape:connector-curvature="0"
id="path4170"
d="m 80,1032.3622 0,20 20,0 0,-20 -20,0 z"
style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-opacity:1"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
webroot/img/duke.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

BIN
webroot/img/lena.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

BIN
webroot/img/lena.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

BIN
webroot/img/lena.tif Normal file

Binary file not shown.

BIN
webroot/img/lena.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
webroot/img/planet.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

BIN
webroot/img/tower.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
webroot/img/webp/1.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -5,46 +5,102 @@
* config-file imgtest_config.php.
*
*/
return array(
/**
* Change to true to enable debug mode which logs additional information
* to file. Only use for test and development. You must create the logfile
* and make it writable by the webserver or log entries will silently fail.
*
* CIMAGE_DEBUG will be false by default, if its not defined.
*/
if (!defined("CIMAGE_DEBUG")) {
define("CIMAGE_DEBUG", false);
//define("CIMAGE_DEBUG", true);
define("CIMAGE_DEBUG_FILE", "/tmp/cimage");
}
return [
/**
* Set mode as 'strict', 'production' or 'development'.
*
* development: Development mode with verbose error reporting. Option
* &verbose and &status enabled.
* production: Production mode logs all errors to file, giving server
* error 500 for bad usage. Option &verbose and &status
* disabled.
* strict: Strict mode logs few errors to file, giving server error
* 500 for bad usage. Stripped from comments and spaces.
* Option &verbose and &status disabled.
*
* Default values:
* mode: 'production'
*/
//'mode' => 'production', // 'development', 'strict'
//'mode' => 'development', // 'development', 'strict'
//'mode' => 'production',
'mode' => 'development',
//'mode' => 'strict',
/**
* Where are the sources for the classfiles.
* Where to find the autoloader.
*
* Default values:
* autoloader: null // used from v0.6.2
* cimage_class: null // used until v0.6.1
* autoloader: null
*/
'autoloader' => __DIR__ . '/../autoload.php',
//'cimage_class' => __DIR__ . '/../CImage.php',
//'autoloader' => __DIR__ . '/../autoload.php',
'autoloader' => __DIR__ . '/../vendor/autoload.php',
/**
* Paths, where are the images stored and where is the cache.
* Path to aliases, useful when downloading external images and you
* want to create a local copy of the file, a alias file.
* End all paths with a slash.
*
* Default values:
* image_path: __DIR__ . '/img/'
* cache_path: __DIR__ . '/../cache/'
* alias_path: null
* image_path: __DIR__ . '/img/'
* cache_path: __DIR__ . '/../cache/'
* alias_path: null
*/
'image_path' => __DIR__ . '/img/',
'cache_path' => __DIR__ . '/../cache/',
'image_path' => __DIR__ . '/img/',
'cache_path' => __DIR__ . '/../cache/',
//'alias_path' => __DIR__ . '/img/alias/',
/**
* Fast track cache. Save a json representation of the image as a
* fast track to the cached version of the image. This avoids some
* processing and allows for quicker load times of cached images.
*
* Default values:
* fast_track_allow: false
*/
//'fast_track_allow' => true,
/**
* Class names to use, to ease dependency injection. You can change Class
* name if you want to use your own class instead. This is a way to extend
* the codebase.
*
* Default values:
* CImage: \Mos\CImage\CImage
* CCache: \Mos\CImage\CCache
* CFastTrackCache: \Mos\CImage\CFastTrackCache
*/
//'CImage' => '\Mos\CImage\CImage',
//'CCache' => '\Mos\CImage\CCache',
//'CFastTrackCache' => '\Mos\CImage\CFastTrackCache',
/**
* Use password to protect from missusage, send &pwd=... or &password=..
* with the request to match the password or set to false to disable.
@@ -87,13 +143,28 @@ return array(
* remote_whitelist: null // use default values from CImage which is to
* // allow download from any hosts.
*/
//'remote_allow' => true,
//'remote_pattern' => '#^https?://#',
//'remote_whitelist' => array(
// '\.facebook\.com$',
// '^(?:images|photos-[a-z])\.ak\.instagram\.com$',
// '\.google\.com$'
//),
/*
'remote_allow' => true,
'remote_pattern' => '#^https?://#',
'remote_whitelist' => [
'\.facebook\.com$',
'^(?:images|photos-[a-z])\.ak\.instagram\.com$',
'\.google\.com$'
], */
/**
* Use backup image if src-image is not found on disk. The backup image
* is only available for local images and based on wether the original
* image is found on disk or not. The backup image must be a local image
* or the dummy image.
*
* Default value:
* src_alt: null //disabled by default
*/
//'src_alt' => 'car.png',
//'src_alt' => 'dummy',
@@ -114,32 +185,44 @@ return array(
* when saving images.
*
* Default value:
* jpg_quality: null, integer between 0-100
* png_compression: null, integer between 0-9
* jpg_quality: null, integer between 0-100,
* default is 60
* png_compression: null, integer between 0-9,
* default is -1 (PHP GD decides)
*/
//'jpg_quality' => 75,
//'png_compression' => 1,
//'jpg_quality' => 60,
//'png_compression' => -1,
/**
* Convert the image to srgb before processing. Saves the converted
* image in the sub cache dir. This option is default false but can
* be changed to default true to do this conversion for all images.
* Convert the image to sRGB before processing. Saves the converted
* image in a cache subdir 'srgb'. This option is default false but can
* be changed to default true to always do conversion for all images.
* This option requires PHP extension imagick and will silently fail
* if that is not installed.
*
* Default value:
* srgb_dir: $cachePath/srgb
* srgb_default: false
* srgb_colorprofile: __DIR__ . '/../icc/sRGB_IEC61966-2-1_black_scaled.icc'
*/
//'srgb_dir' => __DIR__ . '/../cache/srgb/',
//'srgb_default' => false,
//'srgb_colorprofile' => __DIR__ . '/../icc/sRGB_IEC61966-2-1_black_scaled.icc',
/**
* Set skip-original to true to always process the image and use
* the cached version. Default is false and to use the original
* image when its no processing needed.
*
* Default value:
* skip_original: false
*/
//'skip_original' => true,
/**
* A function (hook) can be called after img.php has processed all
* configuration options and before processing the image using CImage.
@@ -170,30 +253,27 @@ return array(
/**
* The name representing a dummy image which is automatically created
* and stored at the defined path. The dummy image can then be used
* inplace of an original image as a placeholder.
* The dummy_dir must be writable and it defaults to a subdir of the
* cache directory.
* Write protect the dummy_dir to prevent creation of new dummy images,
* but continue to use the existing ones.
* and stored as a image in the dir CACHE_PATH/dummy. The dummy image
* can then be used as a placeholder image.
* The dir CACHE_PATH/dummy is automatically created when needed.
* Write protect the CACHE_PATH/dummy to prevent creation of new
* dummy images, but continue to use the existing ones.
*
* Default value:
* dummy_enabled: true as default, disable dummy feature by setting
* to false.
* dummy_filename: 'dummy' use this as ?src=dummy to create a dummy image.
* dummy_dir: Defaults to subdirectory of 'cache_path',
* named the same as 'dummy_filename'
*/
//'dummy_enabled' => true,
//'dummy_filename' => 'dummy',
//'dummy_dir' => 'some writable directory',
/**
* Check that the imagefile is a file below 'image_path' using realpath().
* Security constraint to avoid reaching images outside image_path.
* This means that symbolic links to images outside the image_path will fail.
* This means that symbolic links to images outside the image_path will
* fail.
*
* Default value:
* image_path_constraint: true
@@ -246,8 +326,16 @@ return array(
* Post processing of images using external tools, set to true or false
* and set command to be executed.
*
* The png_lossy can alos have a value of null which means that its
* enabled but not used as default. Each image having the option
* &lossy will be processed. This means one can individually choose
* when to use the lossy processing.
*
* Default values.
*
* png_lossy: false
* png_lossy_cmd: '/usr/local/bin/pngquant --force --output'
*
* png_filter: false
* png_filter_cmd: '/usr/local/bin/optipng -q'
*
@@ -258,7 +346,10 @@ return array(
* jpeg_optimize_cmd: '/usr/local/bin/jpegtran -copy none -optimize'
*/
/*
'postprocessing' => array(
'postprocessing' => [
'png_lossy' => null,
'png_lossy_cmd' => '/usr/local/bin/pngquant --force --output',
'png_filter' => false,
'png_filter_cmd' => '/usr/local/bin/optipng -q',
@@ -267,7 +358,7 @@ return array(
'jpeg_optimize' => false,
'jpeg_optimize_cmd' => '/usr/local/bin/jpegtran -copy none -optimize',
),
],
*/
@@ -277,13 +368,13 @@ return array(
* offset.
*
* Default values.
* convolution_constant: array()
* convolution_constant: []
*/
/*
'convolution_constant' => array(
'convolution_constant' => [
//'sharpen' => '-1,-1,-1, -1,16,-1, -1,-1,-1, 8, 0',
//'sharpen-alt' => '0,-1,0, -1,5,-1, 0,-1,0, 1, 0',
),
],
*/
@@ -300,13 +391,13 @@ return array(
*
* Default values.
* allow_hotlinking: true
* hotlinking_whitelist: array()
* hotlinking_whitelist: []
*/
/*
'allow_hotlinking' => false,
'hotlinking_whitelist' => array(
'hotlinking_whitelist' => [
'^dbwebb\.se$',
),
],
*/
@@ -314,14 +405,14 @@ return array(
* Create custom shortcuts for more advanced expressions.
*
* Default values.
* shortcut: array(
* shortcut: [
* 'sepia' => "&f=grayscale&f0=brightness,-10&f1=contrast,-20&f2=colorize,120,60,0,0&sharpen",
* )
* ]
*/
/*
'shortcut' => array(
'shortcut' => [
'sepia' => "&f=grayscale&f0=brightness,-10&f1=contrast,-20&f2=colorize,120,60,0,0&sharpen",
),*/
],*/
@@ -344,10 +435,10 @@ return array(
'size_constant' => function () {
// Set sizes to map constant to value, easier to use with width or height
$sizes = array(
'w1' => 613,
'w2' => 630,
);
$sizes = [
'w1' => 613,
'w2' => 630,
];
// Add grid column width, useful for use as predefined size for width (or height).
$gridColumnWidth = 30;
@@ -370,7 +461,7 @@ return array(
* aspect_ratio_constant: As the function below.
*/
/*'aspect_ratio_constant' => function () {
return array(
return [
'3:1' => 3/1,
'3:2' => 3/2,
'4:3' => 4/3,
@@ -378,13 +469,13 @@ return array(
'16:10' => 16/10,
'16:9' => 16/9,
'golden' => 1.618,
);
];
},*/
/**
* default options for ascii image.
* Default options for ascii image.
*
* Default values as specified below in the array.
* ascii-options:
@@ -394,11 +485,11 @@ return array(
* luminanceStrategy: Choose any strategy available in CAsciiArt.
* customCharacterSet: Define your own character set.
*/
/*'ascii-options' => array(
/*'ascii-options' => [
"characterSet" => 'two',
"scale" => 14,
"luminanceStrategy" => 3,
"customCharacterSet" => null,
);
},*/
);
],*/
];

View File

@@ -10,6 +10,7 @@
* @link https://github.com/mosbth/cimage
*
*/
define("CIMAGE_BUNDLE", true);
/**

File diff suppressed because it is too large Load Diff

91
webroot/imgf.php Normal file
View File

@@ -0,0 +1,91 @@
<?php
/**
* Fast track cache, read entries from the cache before processing image
* the ordinary way.
*/
// Load the config file or use defaults
$configFile = __DIR__
. "/"
. basename(__FILE__, ".php")
. "_config.php";
if (is_file($configFile) && is_readable($configFile)) {
$config = require $configFile;
} elseif (!isset($config)) {
$config = array(
"fast_track_allow" => true,
"autoloader" => __DIR__ . "/../autoload.php",
"cache_path" => __DIR__ . "/../cache/",
);
}
// Make CIMAGE_DEBUG false by default, if not already defined
if (!defined("CIMAGE_DEBUG")) {
define("CIMAGE_DEBUG", false);
}
// Debug mode needs additional functions
if (CIMAGE_DEBUG) {
require $config["autoloader"];
}
// Cache path must be valid
$cacheIsReadable = is_dir($config["cache_path"]) && is_readable($config["cache_path"]);
if (!$cacheIsReadable) {
die("imgf.php: Cache is not readable, check path in configfile.");
}
// Prepare to check if fast cache should be used
$cachePath = $config["cache_path"] . "/fasttrack";
$query = $_GET;
// Do not use cache when no-cache is active
$useCache = !(array_key_exists("no-cache", $query) || array_key_exists("nc", $query));
// Only use cache if enabled by configuration
$useCache = $useCache && isset($config["fast_track_allow"]) && $config["fast_track_allow"] === true;
// Remove parts from querystring that should not be part of filename
$clear = array("nc", "no-cache");
foreach ($clear as $value) {
unset($query[$value]);
}
// Create the cache filename
arsort($query);
$queryAsString = http_build_query($query);
$filename = md5($queryAsString);
$filename = "$cachePath/$filename";
// Check cached item, if any
if ($useCache && is_readable($filename)) {
$item = json_decode(file_get_contents($filename), true);
if (is_readable($item["source"])) {
foreach ($item["header"] as $value) {
header($value);
}
if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])
&& strtotime($_SERVER["HTTP_IF_MODIFIED_SINCE"]) == $item["last-modified"]) {
header("HTTP/1.0 304 Not Modified");
if (CIMAGE_DEBUG) {
trace("imgf 304");
}
exit;
}
foreach ($item["header-output"] as $value) {
header($value);
}
if (CIMAGE_DEBUG) {
trace("imgf 200");
}
readfile($item["source"]);
exit;
}
}
// No fast track cache, proceed as usual
include __DIR__ . "/img.php";

1
webroot/imgf_config.php Symbolic link
View File

@@ -0,0 +1 @@
img_config.php

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,58 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing option no-upscale";
// Provide a short description of the testcase.
$description = "Do not upscale image when original image (slice) is smaller than target image.";
// Use these images in the test
$images = array(
'car.png',
'apple.jpg',
'tower.jpg',
);
// For each image, apply these testcases
$nc = "&bgc=660000&nc"; //null; //"&nc"; //null; //&nc';
$testcase = array(
$nc . '&w=600',
$nc . '&w=600&no-upscale',
$nc . '&h=420',
$nc . '&h=420&no-upscale',
$nc . '&w=600&h=420',
$nc . '&w=600&h=420&no-upscale',
$nc . '&w=700&h=420&stretch',
$nc . '&w=700&h=420&no-upscale&stretch',
$nc . '&w=700&h=200&stretch',
$nc . '&w=700&h=200&no-upscale&stretch',
$nc . '&w=250&h=420&stretch',
$nc . '&w=250&h=420&no-upscale&stretch',
$nc . '&w=700&h=420&crop-to-fit',
$nc . '&w=700&h=420&no-upscale&crop-to-fit',
$nc . '&w=700&h=200&crop-to-fit',
$nc . '&w=700&h=200&no-upscale&crop-to-fit',
$nc . '&w=250&h=420&crop-to-fit',
$nc . '&w=250&h=420&no-upscale&crop-to-fit',
$nc . '&w=600&h=500&fill-to-fit',
$nc . '&w=600&h=500&no-upscale&fill-to-fit',
$nc . '&w=250&h=420&fill-to-fit',
$nc . '&w=250&h=420&no-upscale&fill-to-fit',
$nc . '&w=700&h=420&fill-to-fit',
$nc . '&w=700&h=420&no-upscale&fill-to-fit',
);
// Apply testcases and present results
include __DIR__ . "/template.php";

View File

@@ -0,0 +1,43 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing resize landscape image";
// Provide a short description of the testcase.
$description = "Resize landscape image";
// Use these images in the test
$images = array(
'car.png',
);
// For each image, apply these testcases
$nc = empty($_SERVER['QUERY_STRING']) ? "" : "&" . $_SERVER['QUERY_STRING'];
$testcase = array(
$nc . '&w=500',
$nc . '&h=200',
$nc . '&w=500&h=500',
$nc . '&w=500&h=200',
$nc . '&w=500&h=200&crop-to-fit',
$nc . '&w=200&h=500&crop-to-fit',
$nc . '&w=500&h=200&fill-to-fit',
$nc . '&w=200&h=500&fill-to-fit',
$nc . '&w=500&h=200&stretch',
$nc . '&w=200&h=500&stretch',
);
// Apply testcases and present results
include __DIR__ . "/template.php";

View File

@@ -0,0 +1,43 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing resize portrait image";
// Provide a short description of the testcase.
$description = "Resize portrait image";
// Use these images in the test
$images = array(
'kodim04.png',
);
// For each image, apply these testcases
$nc = empty($_SERVER['QUERY_STRING']) ? "" : "&" . $_SERVER['QUERY_STRING'];
$testcase = array(
$nc . '&w=500',
$nc . '&h=200',
$nc . '&w=500&h=500',
$nc . '&w=500&h=200',
$nc . '&w=500&h=200&crop-to-fit',
$nc . '&w=200&h=500&crop-to-fit',
$nc . '&w=500&h=200&fill-to-fit',
$nc . '&w=200&h=500&fill-to-fit',
$nc . '&w=500&h=200&stretch',
$nc . '&w=200&h=500&stretch',
);
// Apply testcases and present results
include __DIR__ . "/template.php";

View File

@@ -1,58 +0,0 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing option no-upscale";
// Provide a short description of the testcase.
$description = "Do not upscale image when original image (slice) is smaller than target image.";
// Use these images in the test
$images = array(
'car.png',
);
// For each image, apply these testcases
$nc = null; //"&nc"; //null; //&nc';
$testcase = array(
$nc . '&w=600',
$nc . '&w=600&no-upscale',
$nc . '&h=400',
$nc . '&h=400&no-upscale',
$nc . '&w=600&h=400',
$nc . '&w=600&h=400&no-upscale',
$nc . '&w=700&h=400&stretch',
$nc . '&w=700&h=400&no-upscale&stretch',
$nc . '&w=700&h=200&stretch',
$nc . '&w=700&h=200&no-upscale&stretch',
$nc . '&w=300&h=400&stretch',
$nc . '&w=300&h=400&no-upscale&stretch',
$nc . '&w=600&h=400&crop-to-fit',
$nc . '&w=600&h=400&no-upscale&crop-to-fit',
$nc . '&w=600&h=200&crop-to-fit',
$nc . '&w=600&h=200&no-upscale&crop-to-fit',
$nc . '&w=300&h=400&crop-to-fit',
$nc . '&w=300&h=400&no-upscale&crop-to-fit',
$nc . '&w=600&h=400&fill-to-fit',
$nc . '&w=600&h=400&no-upscale&fill-to-fit',
/*
$nc . '&w=600&ar=1.6',
$nc . '&w=600&ar=1.6&no-upscale',
$nc . '&h=400&ar=1.6',
$nc . '&h=400&ar=1.6&no-upscale',
*/
);
// Apply testcases and present results
include __DIR__ . "/template.php";