Compare commits

..

679 Commits

Author SHA1 Message Date
4238bc0adb Update ChangeLog
Change-Id: I598aaf69c1a45a694c36f2f3166ed9adc20ace84
2012-08-15 22:49:03 -07:00
c655380c36 dec/io.c: cosmetics
- deindent EmitAlphaRGB*
- add some missing consts

Change-Id: I65f88da295e6a0afa383fadc2ef90a40613c2d62
2012-08-15 10:27:54 -07:00
fe1958f17d RGBA4444: harmonize lossless/lossy alpha values
lossy was rounding with a bias toward opaque:
[232+, 8] -> [15, 1]
now both paths use the range:
[240+, 16] -> [15, 1]

Change-Id: I3da2063b4959b9e9f45bae09e640acc1f43470c5
2012-08-14 14:02:30 -07:00
681cb30ad2 fix RGBA4444 output w/fancy upsampling
compensates for the 1-line delay in the upscaler, outputting the correct
alpha row

Change-Id: Ia9a65bcb3cfa19e42185523cc6e706101a39d45d
2012-08-14 13:11:53 -07:00
f06c1d8f7b Merge "Alignment fix" into 0.2.0 2012-08-09 16:09:58 -07:00
f56e98fd11 Alignment fix
Change-Id: Ia5475247f03456b01571ae7531da90f74c068045
2012-08-10 02:10:32 +05:30
6fe843baeb avoid rgb-premultiply if there's only trivial alpha values
With this, MODE_rgbA can safely be used without speed penalty
even in case of pure-lossy alpha-less input.
It's also an optimization when cropping a fully-opaque region from
an image with alpha: premultiply is then skipped

Change-Id: Ibee28c75744f193dacdfccd5a2e7cd1e44604db6
2012-08-09 11:33:29 -07:00
528a11af35 fix the ARGB4444 premultiply arithmetic
* green was not descaled properly
* alpha was over-dithered, making the value '0x0f' not be a fixed point
* alpha value was not restored ok.

Change-Id: Ia4a4d75bdad41257f7c07ef76a487065ac36fede
2012-08-09 11:32:30 -07:00
a0a488554d Lossless decoder fix for a special transform order
Fix the lossless decoder for the case when it has to apply other
inverse transforms before applying Color indexing inverse transform.

The main idea is to make ColorIndexingInverse virtually in-place: we
use the fact that the argb_cache is allocated to accommodate all
*unpacked* pixels of a macro-row, not just *packed* pixels.

Change-Id: I27f11f3043f863dfd753cc2580bc5b36376800c4
2012-08-08 23:52:08 -07:00
62dd9bb242 Update encoding heuristic w.r.t palette colors.
Added a threshold of MAX_COLORS_FOR_GRAPH for color-palettes, above
which the graph hint is ignored.

Change-Id: Ia5d7f45e52731b6eaf2806999d6be82861744fd3
2012-08-08 18:57:52 -07:00
6f4272b090 remove unused ApplyInverseTransform()
transforms are only allowed for is_level0

Change-Id: Iec8ce8bdbe024aae6cae2688e2934ab8f445000c
2012-08-07 22:41:25 -07:00
93bf0faafa Update ChangeLog
Change-Id: I5ff337065b8a6f8952dc77c3f9c7798267ee6727
2012-08-03 16:21:12 -07:00
5934fc59db update AUTHORS
Change-Id: I205422ac3be5e363adfc84dcf84f6d5d84b9a40f
2012-08-03 16:15:38 -07:00
014a711d96 update NEWS
changes since v0.1.99

Change-Id: Iaab1545516ef8df9f9dd6b4bc9cbf07539cb454f
2012-08-03 16:13:29 -07:00
43b0d6107a add support for ARGB -> YUVA conversion for lossless decoder
This was returning an (hard-to-explain) error before.
(through WebPDecodeYUV() for instance).

+ rationalize the incremental API:
-> add WebPINewYUVA
-> deprecated WebPINewYUV
-> add WebPIDecGetYUVA
-> deprecated WebPIDecGetYUV

+ some NULL cosmetics

Change-Id: I39a6bd6018a34294d898b29f6c40e2cf76f1037e
2012-08-03 15:41:01 -07:00
33705ca093 bump version to 0.2.0
Change-Id: I01cb50b9c4c8e9245aede3947481cbbd27d6a19d
2012-08-03 15:41:01 -07:00
c40d7ef125 fix alpha-plane check + add extra checks
Change-Id: I9d8c9743f9d4f3d394544773ed2d0c31a9acf24d
2012-08-03 14:44:35 -07:00
a06f802325 MODE_YUVA: set alpha to opaque if the image has none
this change avoids returning uninitialized alpha values when decoding
lossy with alpha to YUVA

Change-Id: I1e02459ac28b36f1f2b422063d057a5faba2f8f2
2012-08-03 12:04:44 -07:00
52a87dd7ff Merge "silence one more warning" into 0.2.0 2012-08-02 17:53:04 -07:00
3b02309347 silence one more warning
inadvertently added in last warning roundup

Change-Id: I38e6bcfb18c133f2dc2b38cec81e12d2ff556011
2012-08-02 17:50:12 -07:00
f94b04f045 move some RGB->YUV functions to yuv.h
will be needed later

Change-Id: I6b9e460db2d398b9fecd5d3c1bbdb3f2f3d4f5db
2012-08-02 17:23:02 -07:00
4b71ba035a README: sync [cd]webp help output
Change-Id: Ic54e0f3e5e2e667adb369321e5849890d3b96e42
dwebp: -pam, -alpha
2012-08-02 16:11:02 -07:00
c9ae57f596 man/dwebp.1: add links to output file format details
Change-Id: I30e3e52e428c9e68ba2ec263024a1edc56ad6741
2012-08-02 15:10:31 -07:00
292ec5cc7d quiet a few 'uninitialized' warnings
spurious in this case, but addresses e.g.,
... potentially uninitialized local variable 'weighted_average' used

Change-Id: Ib99998bf49e4af7a82ee66f13fb850ca5b17dc71
2012-08-02 14:03:30 -07:00
4af3f6c4d3 fix indentation
Change-Id: Ib00b3cdc21ac336a56390f1e71c169e7fd4767a6
2012-08-02 11:55:55 -07:00
9b261bf521 remove the last NOT_HAVE_LOG2 instances
Change-Id: I193ecf82316cd1d5d7ddeebebf8fc98afccf0ede
2012-08-02 06:42:41 -07:00
323dc4d9b9 remove use of log2(). Use VP8LFastLog2() instead.
Order-by-cost mostly unchanged (up to a scaling constant 1/log(2))
(except for few minor diff in < 2% of cases)

+ remove unused field cost_mode->cache_bits_

Change-Id: I714f8ab12f49a23f5d499a64c741382c9b489a3e
2012-08-02 00:08:58 -07:00
8c515d54ea Merge "harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc" into 0.2.0 2012-08-01 18:16:46 -07:00
d4b4bb0248 Merge changes I46090628,I1a41b2ce into 0.2.0
* changes:
  check VP8LBitWriterInit return
  lossless: fix crash on user abort
2012-08-01 13:19:32 -07:00
bff34ac1ca harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc
quite a large security sweep.

Change-Id: If150dfbb46e6e9b56210473a109c8ad6ccd0cea4
2012-08-01 12:06:04 -07:00
a3c063c714 Merge "extra size check for security" into 0.2.0 2012-08-01 12:00:30 -07:00
5e7963000a Merge "WebPEncode: clear stats at the start of encode" into 0.2.0 2012-08-01 12:00:09 -07:00
f1edf62fae Merge "rationalize use of color-cache" into 0.2.0 2012-08-01 11:55:36 -07:00
c19333173a extra size check for security
no speed diff observed by removing the test before calling BitWriterResize().

+ remove some unnecessary memset() in VP8LBitWriter
+ fix mixed code/variable-decl in BIG_ENDIAN mode

Change-Id: I36be61f83d10a43e4682b680c2dae0e494da4218
2012-08-01 00:37:24 -07:00
906be65744 rationalize use of color-cache
* ~1-4% faster
* if it's not used, don't use it
* remove the special handling of cache_bits = 0
* remove some tests in the loops

Change-Id: I19d87c3ca731052ff532ea8b2d8e89816507b75f
2012-08-01 00:32:12 -07:00
dd1c3873fe Add image-hint for low-color images.
For low-color images, it may be better to not use color-palettes.
Users should treat this as one another hint (as with Photo &
Picture) and another parameter for tuning the compression density.
The optimum compression can still be obtained by running (outer loop)
compression with all possible tunable parameters.

Change-Id: Icb1a4face2a84774e16e801aee4a8ae97e232e8a
2012-07-31 23:11:19 -07:00
4eb7aa64da Merge "WebPCheckMalloc() and WebPCheckCalloc():" into 0.2.0 2012-07-31 18:37:14 -07:00
80cc7303ab WebPCheckMalloc() and WebPCheckCalloc():
safe size-checking versions of malloc() and calloc()

Change-Id: Iffa3138c48b9b254b3d7eaad913e1f852d9dafba
2012-07-31 16:56:39 -07:00
183cba83a7 check VP8LBitWriterInit return
Change-Id: I460906281598f5792bd75a25b14b449c8daaff8c
2012-07-31 12:11:40 -07:00
cbfa9eecf4 lossless: fix crash on user abort
avoid free on uninitialized bit writer buffer

Change-Id: I1a41b2cea421bf5a2ea0af33c6e84018cb997caf
2012-07-31 11:59:54 -07:00
256afefa50 cwebp: exit immediately on version mismatch
{Picture,Config}Init don't do allocations and attempting to free any of
their contents on version mismatch will likely cause a crash

Change-Id: I2a5aece235f9680fb406aec4799adceea7f62cfc
2012-07-27 19:58:58 -07:00
475d87d767 WebPEncode: clear stats at the start of encode
also relocate user_data from WebPAuxStats to the WebPPicture struct to
make clearing easier while placing it closer to the progress hook with
which it's used.
prior to this change some spurious lossless data could be reported in
the lossy (sans alpha) encoding case. additionally user_data could be
lost during lossless encoding.

Change-Id: I929fae3dfde4d445ff81bbaad51445ea586dd80b
2012-07-27 19:57:18 -07:00
a7cc729126 fix type and conversion warnings
avoids warning messages on MSVC mainly

Change-Id: I80f281d5263a54c6a224bb095175497cf2f4ce1e
2012-07-25 14:18:21 -07:00
7d853d79dc add stats for lossless
* Extend AuxStats with new fields
  it's slightly ABI-incompatible, but i guess it's ok for 0.1.99+
  I expect to add more stats later, possibly (predictor stats, etc.)
* Have cwebp report the features used by lossless
  compression (either for alpha or full lossless coding)
* Print the PSNR for alpha (useful in case of -alpha_q)
* clean-up alpha.c signatures
+ misc cleanup (added const '* const ptr', etc.)

Change-Id: I157a21581f1793cb0c6cc0882e7b0a2dde68a970
2012-07-24 16:17:13 -07:00
d39177b74c make QuantizeLevels() store the sum of squared error
(instead of MSE).
Useful for directly storing the alpha-PSNR (in another patch)

Change-Id: I4072864f9c53eb4f38366e8025a2816eb14f504e
2012-07-24 15:06:18 -07:00
5955cf5e89 replace x*155/100 by x*101581>>16
Don't expect a visible speed diff. it's just cool.
(and, that's one less TODO in the code).

Change-Id: Iaeb2f1c930debb51501e170ee806f2f945fb1a8d
2012-07-24 15:06:00 -07:00
7d732f905b make QuantizeLevels() store the sum of squared error
(instead of MSE).
Useful for directly storing the alpha-PSNR (in another patch)

Change-Id: I4072864f9c53eb4f38366e8025a2816eb14f504e
2012-07-23 14:26:56 -07:00
e45a446ad5 replace x*155/100 by x*101581>>16
Don't expect a visible speed diff. it's just cool.
(and, that's one less TODO in the code).

Change-Id: Iaeb2f1c930debb51501e170ee806f2f945fb1a8d
2012-07-23 14:23:33 -07:00
159b75d31a cwebp output size consistency:
In case of lossless too, it should report full file size.
Fixes this issue: http://code.google.com/p/webp/issues/detail?id=126

Change-Id: I96e2bf09e6c9470a0267f5eea911d9b40d1addb3
2012-07-23 12:47:24 +05:30
cbee59eba4 Merge commit 'v0.1.99'
* commit 'v0.1.99': (39 commits)
  Update ChangeLog
  add extra precision about default values and behaviour
  header/doc clean up
  Makefile.vc: fix webpmux.exe *-dynamic builds
  remove INAM, ICOP, ... chunks from the test webp file.
  harmonize authors as "Name (mail@address)"
  makefile.unix: provide examples/webpmux target
  update NEWS
  README: cosmetics
  man/cwebp.1: wording, change the date
  add a very crude progress report for lossless
  rename 'use_argb_input' to 'use_argb'
  add some padding bytes areas for later use
  fixing the findings by Frederic Kayser to the bitstream spec
  add missing ABI compatibility checks
  Doc: container spec text tweaks
  add ABI compatibility check
  mux.h: remove '* const' from function parameters
  encode.h: remove '* const' from function parameters
  decode.h: remove '* const' from function parameters
  ...

Conflicts:
	src/mux/muxinternal.c

Change-Id: I635d095c451742e878088464fe6232637a331511
2012-07-21 12:20:19 -07:00
1889e9b6cc dwebp: report -alpha option
remove from WEBP_EXPERIMENTAL_FEATURES block; alpha is no longer
experimental.

Change-Id: I57df006ecac8122a987e52084813dc84ca7bcfd6
2012-07-20 19:42:56 -07:00
3bc3f7c0ee Merge "dwebp: add PAM output support" into 0.2.0 2012-07-20 19:09:45 -07:00
d919ed06eb dwebp: add PAM output support
retains the alpha channel rather than stripping it as with PPM.

display from ImageMagick can render the files

Change-Id: I4f3a5d332937e0aeaf4e3fbd214fdae3b5382fb8
2012-07-20 19:06:01 -07:00
85e215d36f README/manpages/configure: update website link
code.google was moved to developers.google

Change-Id: I072cab38ccb6f45c3d1d6e533d1626420cdbba56
2012-07-20 16:25:04 -07:00
c3a207b9f4 Update ChangeLog
Change-Id: I7e8192f4c5fd90354669c492235b1348debdc839
2012-07-19 18:19:33 -07:00
d1fd78263f Merge "add extra precision about default values and behaviour" into 0.2.0 2012-07-19 18:15:45 -07:00
efc826e04a add extra precision about default values and behaviour
Change-Id: I445f4d3b20a53d32819fe361f74443e0a0c8a632
2012-07-19 18:08:42 -07:00
9f29635d95 header/doc clean up
Put emphasis on RGBA decoding instead of RGB/BGR
Clarify doc at some rough spots
Misc typo fix and cosmetics

Change-Id: Ic5fcfcc5bf4d612c5de23b0a5499f1fadde55bfe
2012-07-19 18:05:03 -07:00
ff9fd1bac6 Makefile.vc: fix webpmux.exe *-dynamic builds
the libwebpmux objects depend on the generated webp_dll.[hc] files as
well

Change-Id: I26177b56f415d69e4bc34b24d32c70a13c6c053c
2012-07-19 17:55:50 -07:00
8aacc7b056 remove INAM, ICOP, ... chunks from the test webp file.
Change-Id: Ia396d932b8b1af85717f693251c76985abc86395
2012-07-19 17:34:04 -07:00
2fc1301577 harmonize authors as "Name (mail@address)"
Change-Id: I85bfae61a37de75a5ed945a906002de2ef75149f
2012-07-19 16:09:47 -07:00
4a9f37b742 Merge "update NEWS" into 0.2.0 2012-07-19 14:44:06 -07:00
7415ae1386 makefile.unix: provide examples/webpmux target
this target is experimental so must be explicitly set

missing since:
e41a759 build: remove libwebpmux from default targets/config

Change-Id: I128d9e6a06ba3e7102ae3175dc3db52c6f4b1439
2012-07-19 14:39:26 -07:00
ce82cedc4b update NEWS
mention TIFF support, cwebp's alpha encoding disposition

Change-Id: I0340590f43e1617c6fb3fc2e98a609dbf4a880ce
2012-07-19 12:59:39 -07:00
641e28e8dc Merge "man/cwebp.1: wording, change the date" into 0.2.0 2012-07-19 12:58:13 -07:00
c37c23e594 README: cosmetics
- update swig function references
- remove references to decode_vp8.h (no longer installed)
- add lossless references
- some grammar/spelling changes

Change-Id: Icebfbcf6f638762f42d844b6bd3c0129c64d9340
2012-07-19 12:38:46 -07:00
3976dcd59f man/cwebp.1: wording, change the date
options were added since the last date refresh

Change-Id: I3899ebd429ba0260a28ce02bb973e1ee015c6e6e
2012-07-19 12:20:01 -07:00
3e5bbe1c2e Merge "rename 'use_argb_input' to 'use_argb'" into 0.2.0 2012-07-18 23:26:42 -07:00
ce90847a40 Merge "add some padding bytes areas for later use" into 0.2.0 2012-07-18 23:26:18 -07:00
2390dabcb6 Merge "fixing the findings by Frederic Kayser to the bitstream spec" into 0.2.0 2012-07-18 23:19:34 -07:00
0275159143 add a very crude progress report for lossless
better than nothing. Removed the warning in cwebp.c
(and silenced the warning in quiet mode too)

Change-Id: I85bbeaf77e0f60ead798886043dc053e6b44def5
2012-07-18 22:54:50 -07:00
a4b9b1c604 Remove some unused enum values.
- WEBP_MUX_INVALID_PARAMETER: was used only at one place, and that too
should actually be an assert().
- WEBP_MUX_ERROR: was never used.

Change-Id: I8883cb4dfae7a7918507501f21fced0c04dda36a
2012-07-19 11:23:37 +05:30
dd1081763c rename 'use_argb_input' to 'use_argb'
long name, and there's not really an 'output' equivalent

Change-Id: I9133ff734ae8d6572cb2f607211361f011fc0bc1
2012-07-18 22:47:16 -07:00
90516ae8f5 add some padding bytes areas for later use
so we can add few fields without breaking ABI

+ re-order fields
+ refresh the doc

Change-Id: Id60ec33934f6346e35c95fcdb4abbe1bc7b50acb
2012-07-18 22:47:13 -07:00
d03b250369 fixing the findings by Frederic Kayser to the bitstream spec
Change-Id: I4bcc428412dd4c21675d90159771e2e0b338ecf3
2012-07-18 22:47:11 -07:00
ce156afccf add missing ABI compatibility checks
original change:
f7f16a2 add ABI compatibility check

Change-Id: I7cd6508f8d8e0d957a3d62ad52a117876fa5ec29
2012-07-18 22:24:33 -07:00
9d45416aa7 Merge "Doc: container spec text tweaks" into 0.2.0 2012-07-18 21:49:24 -07:00
4e2e0a8cac Doc: container spec text tweaks
Change-Id: I8e29f1c814b0c8f6aa268562d8653989caaf281d
modified:   doc/webp-container-spec.txt
2012-07-18 16:38:50 -07:00
f7f16a2976 add ABI compatibility check
minor revision shouldn't matter, we only check major revision number.
Bumped all version numbers so that incompatibility starts *now*

Change-Id: Id06c20f03039845ae4cfb3fd121807b931d67ee4
2012-07-18 11:53:25 -07:00
2a77557002 Merge "swig: add WebPEncodeLossless* wrappers" into 0.2.0 2012-07-17 17:30:17 -07:00
a3ec6225d5 mux.h: remove '* const' from function parameters
makes the public interface consistent and more readable

Change-Id: I33f1d1c4ee752e353e4c10636a4df4e44d7cd03f
2012-07-17 17:21:54 -07:00
31426ebaec encode.h: remove '* const' from function parameters
makes the public interface consistent and more readable

Change-Id: Ib93614e901e0af44bb64782357cfd9e724e050be
2012-07-17 16:26:54 -07:00
9838e5d5ff decode.h: remove '* const' from function parameters
makes the public interface consistent and more readable

Change-Id: I067eb5ecc1094216ef6aecc65f636f69873de8f9
2012-07-17 15:48:00 -07:00
4972302d24 swig: add WebPEncodeLossless* wrappers
Change-Id: If373a5d2953ec53b856900666422fb2b4f9939a4
2012-07-17 14:41:51 -07:00
9ff00cae72 bump encoder/decoder versions
those returned by WebPGet(Encoder|Decoder)Version()

Change-Id: I7584d04060ae7ca552f8f3aaf9df294d310a5be6
2012-07-17 13:43:39 -07:00
c2416c9b61 add lossless quick encoding functions to the public API
New functions:
   WebPEncodeLosslessRGB()
   WebPEncodeLosslessBGR()
   WebPEncodeLosslessRGBA()
   WebPEncodeLosslessBGRA()

Change-Id: Id56da7569eee80c57de8f1f053fb87b217d33a4b
2012-07-17 12:02:53 -07:00
4c1f5d6435 Merge "NEWS: mention decode_vp8.h is no longer installed" into 0.2.0 2012-07-17 11:20:55 -07:00
6cb2277d0f NEWS: mention decode_vp8.h is no longer installed
Change-Id: Id1075b4a917c83552446fb62dada8868dbdb21ef
2012-07-17 11:19:42 -07:00
d5e5ad6356 move decode_vp8.h from webp/ to dec/
the functions contained in it are now private

Change-Id: Ief6c81b32ae3f6d97052edac625716e5b909e66e
2012-07-16 22:12:59 -07:00
8d3b04a25d Merge "header clean-up" into 0.2.0 2012-07-16 19:21:49 -07:00
02201c35a0 Merge "remove one malloc() by making color_cache non dynamic" into 0.2.0 2012-07-16 19:21:05 -07:00
d708ec1452 Merge "move MIN/MAX_HISTO_BITS to format_constants.h" into 0.2.0 2012-07-16 19:19:32 -07:00
ab2da3e9fd Merge "add a malloc() check" into 0.2.0 2012-07-16 19:18:28 -07:00
2d571bd89f add a malloc() check
mostly a sanity measure to be future-proof

Change-Id: I55a81345819b2a8e939c98f0da883dc5c0cc16a2
2012-07-16 19:15:00 -07:00
7f0c178e46 remove one malloc() by making color_cache non dynamic
Change-Id: I7c71a056f79a79bfacfe64a263f1eb8476c05456
2012-07-16 19:13:58 -07:00
6569cd7c88 Merge "VP8LFillBitWindow: use 64-bit path for msvc x64 builds" into 0.2.0 2012-07-16 19:13:30 -07:00
23d34f3142 header clean-up
remove unused VP8LHistogramRemove function
make HistogramEstimateBitsHeader static

Change-Id: I0dc6a6f4c41d3c5f55a8153cd7d710c9f84582a7
2012-07-16 19:13:01 -07:00
2a3ab6f925 move MIN/MAX_HISTO_BITS to format_constants.h
(under the name MAX_HUFFMAN_BITS, since the specs read:
	int huffman_bits = ReadBits(3) + 2;
)

Change-Id: Ifc66576bbd8e48518d3d78a3f515f851cf1883dc
2012-07-16 19:11:54 -07:00
985d3da6c4 Merge "shuffle variables in HashChainFindCopy" into 0.2.0 2012-07-16 19:11:00 -07:00
cdf885c67c shuffle variables in HashChainFindCopy
might lead to observable speed-up on some compiler/arch

Change-Id: I6c89ec9cd4f490e7e22b790785f80097a47d27b5
2012-07-16 19:08:59 -07:00
c3b014db28 Android.mk: add missing lossless files
Change-Id: I5d97e22392ed3847e97f3b31c928d3828fa6732a
2012-07-16 18:43:07 -07:00
8c1cc6b5dc makefile.unix dist: explicitly name installed includes
avoids mux.h, format_constants.h

Change-Id: I6c8458dd1ce7faa23fbf1e3df84a8dfc4a884a3e
2012-07-16 16:32:15 -07:00
7f4647eecb Merge "clarify the colorspace naming and byte ordering of decoded samples" into 0.2.0 2012-07-16 12:16:17 -07:00
cbf69724d2 clarify the colorspace naming and byte ordering of decoded samples
Change-Id: If5ac331bd1e45e7c959b5fa60248268ae2bbe861
2012-07-16 12:09:51 -07:00
857650c8fc Mux: Add WebPDataInit() and remove WebPImageInfo
Change-Id: If661f7d198e284a103a53a451e9f74805119fcf9
2012-07-16 11:12:05 +05:30
ff771e7705 don't install webp/decode_vp8.h
the VP8 decode functions do not need to be public; only
GetInfo/CheckSignature need to be marked extern as they're used by
libwebpmux.

Change-Id: Id9ab4d6166b0271cf5d04563c6dac1fcc84adbdc
2012-07-14 21:23:03 -07:00
596dff784d VP8LFillBitWindow: use 64-bit path for msvc x64 builds
Change-Id: I14a3865f4091dd048e02abc99aa4e7f1e325e12a
2012-07-14 13:20:26 -07:00
3ca7ce9805 Merge "doc: remove non-finalized chunk references" into 0.2.0 2012-07-14 10:58:52 -07:00
1efaa5a369 Merge "bump versions" into 0.2.0 2012-07-14 10:57:58 -07:00
51fa13e170 Merge "README: update cwebp help output" into 0.2.0 2012-07-14 09:29:19 -07:00
12f9aede7e README: update cwebp help output
Change-Id: Ifc2fafca8131e53a894056e37bb22d25fedfa150
2012-07-13 14:55:39 -07:00
f0b5defb0a bump versions
lib - 0.1.99
libtool - 3.0.0 (interface changes)

Change-Id: Id52f8b8df5b941b15af6cb54b1cc05daf322179f
2012-07-13 14:07:51 -07:00
4c42a61b9a update AUTHORS
Change-Id: Ibf5b8c6aa101e6dd6cbcb69485ae582364387d80
2012-07-13 13:55:31 -07:00
6431a1ce3d doc: remove non-finalized chunk references
tiles, animation, etc.

Change-Id: Ie83135be576f516fe475c7be21171f9d12a17ce4
2012-07-13 12:33:49 -07:00
8130c4cc64 Merge "build: remove libwebpmux from default targets/config" 2012-07-13 11:33:52 -07:00
23b44438dd Merge "configure: broaden test for libpng-config" 2012-07-13 11:13:26 -07:00
85bff2cdbe Merge "doc: correct lossless prefix coding table & code" 2012-07-13 11:12:14 -07:00
05108f6ea0 Merge "More spec/code matching in mux:" 2012-07-12 23:53:58 -07:00
6808e69db8 More spec/code matching in mux:
- Match offsets, duration, width/height for frames/tiles and enforce
some constraints.
- Note that this also means using 'int's instead of 'uint32_t's for
16-bit and 24-bit fields.

Change-Id: If0b229ad9fce296372d961104aa36731a3b1304b
2012-07-13 11:47:24 +05:30
bd2b46f5f2 Merge "doc/webp-container-spec: light cosmetics" 2012-07-12 21:04:27 -07:00
20ead3290f doc/webp-container-spec: light cosmetics
- provide anchor links to the bitstream chunks referenced in the
  extended format section
- remove some unnecessary parenthetical notation and add some
  consistency to various chunk element definitions

Change-Id: Ifd946fee81b36e4e555399555a2a58c853fdc3b7
2012-07-12 19:19:39 -07:00
1d40a8bce8 configure: add pthread detection
adds a --disable-threading option to prevent the check.
uses AX_PTHREAD from:
git://git.savannah.gnu.org/autoconf-archive.git
100644 blob d90de34d14    ax_pthread.m4

Change-Id: Icf3ad1ebcf052748bc341706effcc9ba02a259e4
2012-07-12 16:27:29 -07:00
b5e9067a28 fix some int <-> size_t mix for buffer sizes
could have led to some negative overflow on 32bit arch
(if it was not for the "total_size == (size_t)total_size" test)

Change-Id: I7640340b605b9c674d30dd58a1e2144707299683
2012-07-12 07:23:01 -07:00
e41a759615 build: remove libwebpmux from default targets/config
the extended file format is still under development and related
libs/binaries are not fit for release

configure:
--enable/disable-libwebpmux; default is disabled.

makefile.unix:
src/mux/libwebpmux.a and examples/webpmux must be explicitly specified

Makefile.vc:
$(DIRLIB)\libwebpmux.lib and $(DIRBIN)\webpmux.exe must be explicitly
specified

Change-Id: I8246746b256010dd2a2e4de58291222d7eaf0457
2012-07-11 14:25:41 -07:00
0fc2baae5c configure: broaden test for libpng-config
some versions of cygwin's libpng-devel only install libpngXX-config,
e.g., libpng12-config, but do not create libpng-config. the library also
has a version number.

Change-Id: I35332b7011e2dbabb95b599f810523a729d6097e
2012-07-11 14:21:50 -07:00
45b8272c31 Merge "restore authorship to lossless bitstream doc" 2012-07-11 13:56:10 -07:00
06ba05909e restore authorship to lossless bitstream doc
lost in port from pdf

Change-Id: I17bad348577be9818c036e557a0beba63ffcc689
2012-07-11 13:46:00 -07:00
44a09a3ccd add missing description of the alpha filtering methods
Change-Id: Id7bdf5e30121ce5d610b7fa00f64bcd2d7bed2bf
2012-07-10 19:50:30 -07:00
63db87dd2a Merge "vwebp: add checkboard background for alpha display" 2012-07-10 12:38:16 -07:00
a73b8978d1 vwebp: add checkboard background for alpha display
Change-Id: Iefb02f38c0479e61334844d9110cd1b735f0395d
2012-07-10 12:02:03 -07:00
939158ce4f Merge "vwebp: fix info display" 2012-07-10 07:37:28 -07:00
b35c07d9a7 vwebp: fix info display
change alpha intensity to 1 from 0; broken since alpha blending was enabled.

Change-Id: Ic8bb73b62e602ae56e08e9d5a77fdc6aa76b54c5
2012-07-09 22:47:10 -07:00
48b39eb17a fix underflow for very short bitstreams
+ hardened the asserts

Change-Id: Ie798ef2f9d848c131f6f84a35ea28ef254822d1e
2012-07-09 16:47:34 -07:00
7e622984bb cosmetics: param alignment, manpage wording
after eb6f9b8, c0e8859

Change-Id: I79bcea77d65eb6d1abf1156796996f3b9909b62b
2012-07-07 10:37:26 -07:00
1bd7dd5097 Merge changes I7b0afb0d,I7ecc9708
* changes:
  Get rid of image_info_ from WebPChunk struct.
  WebP Container Spec:
2012-07-04 16:32:29 -07:00
ac69e63eeb Merge "Updated cwebp man's help for Alpha & Lossless." 2012-07-04 16:30:03 -07:00
c0e8859d81 Get rid of image_info_ from WebPChunk struct.
The image_info_ was used only in GetImageCanvasWidthHeight(). So, now
we infer it from data there.
This removal fixes a bug: earlier, 'image_info' wasn't initialized in
the WebPMuxCreate() flow, and so the canvas width/height were being
calculated to be zero.

Also, a related refactoring: Combine CreateImageInfo() and
CreateDataFromImageInfo() into a single function CreateFrameTileData().

Change-Id: I7b0afb0d36dc6e13b9d6a1135fb027aa4e03716c
2012-07-04 18:49:56 +05:30
135ca69eb7 WebP Container Spec:
Clarify that a file must contain at least one frame.

Change-Id: I7ecc97084498adc108275585d0c7d0aaa9f5c6ed
2012-07-04 17:28:17 +05:30
eb6f9b8a72 Updated cwebp man's help for Alpha & Lossless.
Change-Id: Iaa129685fdbd95c3b408c92c77532b321bc2637f
2012-07-04 12:51:56 +05:30
0fa844fb8f cosmetic fixes on assert and 'const' where applicable
Change-Id: Iede15b8464be453e7d12929513ed82183921265c
2012-07-03 05:50:36 -07:00
7f22bd2596 check limit of width * height is 32 bits
Update the RIFF-specs file too

Change-Id: I113a7e25da2f7a19c1344c1dc5d496127cfe2596
2012-07-03 05:42:13 -07:00
16c46e83ac autoconf/make: cosmetics: break long lines
Change-Id: If08350a27cdfc4371dcb28ecb9d88d05eceaf977
2012-07-02 21:48:18 -07:00
ab22a07afc configure: add helper macro to define --with-*
replaces repeated --with-{jpeg,png,tiff}{include,lib}dir defines

Change-Id: Ic0eb8622dfc94184ab74f8302112c36547a34664
2012-07-02 21:26:48 -07:00
c17699b343 configure: add libtiff test
Change-Id: Ia5828797bae9c993a3d00ddd5e0089d3b828e975
2012-07-02 18:01:49 -07:00
0e09732c5e Merge "cwebp: fix crash with yuv input + lossless" 2012-07-02 14:40:00 -07:00
88a510ff53 Merge "fix big-endian VP8LWriteBits" 2012-07-02 14:38:32 -07:00
da99e3bfcd Merge "Makefile.vc: split mux into separate lib" 2012-07-02 14:36:17 -07:00
7bda392b50 cwebp: fix crash with yuv input + lossless
Change-Id: Ia27c925f532a4243369288e954abdc063b9f94f9
2012-07-02 14:33:25 -07:00
f56a369ab0 fix big-endian VP8LWriteBits
bits would be lost if n_bits was > 17

Change-Id: Id315d075075338a08e10c04faa91cab347a53591
2012-07-02 14:30:41 -07:00
54169d6cd7 Merge "cwebp: name InputFileFormat members consistently" 2012-07-02 13:19:27 -07:00
e2feefa9b8 Makefile.vc: split mux into separate lib
similar to other builds, reusing some variable naming from
makefile.unix.
also change the main output file names to libwebp{,mux}.{lib,dll} with
the import lib retaining the _dll suffix.

Change-Id: I6bbc4fb39ab37957f153e9f31954387b24094581
2012-07-02 12:37:57 -07:00
27caa5aa34 Merge "cwebp: add basic TIFF support" 2012-07-02 11:39:21 -07:00
d8921dd4e6 cwebp: name InputFileFormat members consistently
append an '_'. TIFF introduced TIFF_ to avoid a name clash.

Change-Id: Iebe484d43aeb8e1912cd5bd10d36e1c5e66a4b42
2012-07-02 11:24:23 -07:00
6f76d246ba cwebp: add basic TIFF support
Change-Id: I29f354e1ef9cf8f23002cf2e15814a53a94b4ed5
2012-07-02 11:22:52 -07:00
4691407bb3 Merge changes If39ab7f5,I3658b5ae
* changes:
  WebPMuxCreate() error handling:
  Fix a memleak in WebPMuxCreate()
2012-07-02 01:46:17 -07:00
cca7c7b81b Fixed nit: 10 -> 10.f
Change-Id: I0cdb096525460b27f3adcb9f802f6dc193d5f590
2012-07-02 11:29:01 +05:30
5d09a244b7 WebPMuxCreate() error handling:
Directly return NULL if no allocation is done yet.

Change-Id: If39ab7f5a55833263d3372fa0a5d9b0a600cb9ed
2012-07-02 11:20:09 +05:30
777341c3d0 Fix a memleak in WebPMuxCreate()
Change-Id: I3658b5ae487082aef28989eb8abc274c207fef0f
2012-07-02 10:19:21 +05:30
61c9d161d5 doc: correct lossless prefix coding table & code
extra bit counts and literal distance return value

Change-Id: I290e3ee8900469503a323f87e9dbb8ca5cb4afc7
2012-07-01 14:10:32 -07:00
4c3975792b Merge "mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN" 2012-06-29 10:31:26 -07:00
e4e36cc6c6 Merge "Mux: Allow only some frames/tiles to have alpha." 2012-06-29 05:17:26 -07:00
ad2aad3c21 Merge "WebP Decoding error handling:" 2012-06-29 05:15:55 -07:00
97649c8f6b Mux: Allow only some frames/tiles to have alpha.
And related const fixes.

Change-Id: I79f6f1b9f8c6faac8cc4ef24b58edff6d90de045
2012-06-29 17:17:19 +05:30
f864be3b2c Lower the quality settings for Alpha encoding.
Evaluated the impact of this change over 1000 image corpus.
The compression density is up (on average) by 1.2% and encoding time has
gone down considerably from 716 ms (per file) to 146 ms (per file)
(4.9X improvement in encoding time).

Change-Id: Ida562cc0bfe18c9d6f5f00873c95f8396b480eab
2012-06-29 16:19:42 +05:30
3ba81bbe8b WebP Decoding error handling:
- Don't allow any extended chunks (except ALPH) without RIFF & VP8X
chunks.
- Also, don't allow VP8X without RIFF.

Change-Id: I1beba43e617ec637901aeeb93f2f484ec086a75d
2012-06-29 14:32:21 +05:30
fcc69923b9 add automatic YUVA/ARGB conversion during WebPEncode()
Adds new methods WebPPictureARGBToYUVA() and WebPPictureYUVAToARGB()
Depending on the value of picture->use_argb_input,
the main call WebPEncode() will convert appropriately.
Note that both conversions are lossy, so it's recommended to:
* use YUVA input for lossy compression (picture->use_argb_input=0)
* use ARGB input for lossless compression (picture->use_argb_input=1)

Change-Id: I8269d607723ee8a1136b9f4999f7ff4e657bbb04
2012-06-28 00:34:23 -07:00
802e012a18 fix compilation in non-FANCY_UPSAMPLING mode
Change-Id: Id0b1fad3a4888b6e9563a227412b2e6a656d9a2a
2012-06-28 00:26:35 -07:00
e012dfd90f make width/height coding match the spec
* width/height in VP8X chunk is 24bit now
* Added some more constants #defines

Change-Id: I2f8ca7f965a247bccd341dd079ed2abf549c39d7
2012-06-28 00:20:28 -07:00
228d96a538 mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN
these functions are used by libwebpmux

Change-Id: Ifcb79e685f3a6b53007c0dc5f220737daba97d47
2012-06-27 16:07:33 -07:00
637a314f97 remove the now unused *KeepA variants
Change-Id: I65217f3075e30bc9a7f38a49d09f01c9d7271d6a
2012-06-27 10:00:48 -07:00
d11f6fcc82 webpmux returns error strings rather than numbers
Change-Id: Ifc8d8aaf0f6c03ecb1508ec7fbc34c918bf69c61
2012-06-27 16:55:57 +05:30
fcec059322 makefile.unix: cwebp: fix OSX link
this is more a workaround than a fix. this change forces the tables
from yuv.h into the data section rather than generating them as common
blocks. prior to this the linkage would fail with e.g.,

Undefined symbols:
...
  "_VP8kClip4Bits", referenced from:
      _Yuv444ToRgba4444 in libwebp.a(libwebpdsp_la-upsampling.o)

broken since:
 48f8275 add colorspace for premultiplied alpha

Change-Id: I69c36758c31cd3a4b3ea5c412674d1a47b45447e
2012-06-26 18:09:55 -07:00
6b811f1b06 Merge "doc: remove lossless pdf" 2012-06-22 15:43:20 -07:00
c963482124 doc: remove lossless pdf
superseded by webp-lossless-bitstream-spec.txt

Change-Id: I5f233d4c3c2fa048a180db950ad4319ffdb9bced
2012-06-22 15:25:03 -07:00
b9ae4f0d88 cosmetics after mux changes b74ed6e, b494ad5
Change-Id: I8f693b1ba2537de89bda1dfbcf3b95abd17e420a
2012-06-22 14:30:01 -07:00
b494ad5096 Mux: only allow adding frame/tiles at the end.
Change-Id: I5d685628da8c8ac85144cee2e5808b30ec79fef9
2012-06-22 11:18:18 -07:00
2c341b0eed Merge "Added image characteristic hint for the codec." 2012-06-22 02:54:51 -07:00
d373076a53 Added image characteristic hint for the codec.
This image type hint is used by codec to apply appropriate set of
transform filters to the image.

Change-Id: Ibb24e47ba14c40fa7f9f2c50ae86e03f2b29e9a6
2012-06-22 14:03:46 +05:30
2ed2adb5ba Merge "msvc: add intrinsic based BitsLog2Floor" 2012-06-22 00:13:47 -07:00
e595e7c552 Merge "add demux.c to the makefiles" 2012-06-21 23:44:44 -07:00
da47b5bda7 Merge "demux: add {Next,Prev}Chunk" 2012-06-21 23:43:03 -07:00
e5f467420d add demux.c to the makefiles
Change-Id: If98c0ea859c845261218196df35e905589f09e76
2012-06-21 23:21:41 -07:00
4708393c3b demux: add {Next,Prev}Chunk
replaces WebPDemuxSetChunk(). makes this consistent with the Frame
interface.

Change-Id: Ia2b1b98cc290f35b41fd14ee35a2a17cecac7ba8
2012-06-21 23:18:39 -07:00
e8a0a821d2 demux: quiet msvc warnings
min_size is safe to be a uint32_t so avoid the size_t -> uint32_t
conversion

Change-Id: Ib53fe58d313b55dfcf3bee35d0cad48f210d07bf
2012-06-21 23:09:38 -07:00
7f8472a610 Update the WebP Container Spec.
- Add details about the VP8L chunk support.
- Also add new example bitsteams containing VP8L chunk.
- Add back a section describing the VP8 chunk.
- Restrict some fields to 16 or 24 bits instead of 32 bits.
- Fields whose values are always positive are stored 1-based
instead of 0-based.
- Unknown chunks can only occur at certain places in the file.
- Remove the restriction for some fields to be divisible by 32 or 16.
  Instead they are restricted to be even.
- Add a restriction for (canvas_width * canvas_height) product.
- Add 3 bits for rotation & symmetry in VP8X flags.
- Add some new example layouts.
- Add/clarify some nitty-gritties throughout the doc.
- Use the terms frame/tile more consistently and logically.
- Update related TODOs.

Change-Id: I611c1f58ecc3ee87546ca31bad1609376fad251e
2012-06-21 16:04:58 -07:00
31b68fe639 cleanup WebPPicture struct and API
* add a real proper pointer for holding memory chunk pointer
  (instead of using y and argb fields)
* polish the doc with details
* add a WebPPictureView() that extract a view from a picture
  without any copy (kind of a fast-crop).
* properly snap the top-left corner for Crop/View. Previously,
  the luma position was not snapped, and was off compared to the chroma.

Change-Id: I8a3620c7f5fc6f7d1f8dd89d9da167c91e237439
2012-06-21 00:30:43 -07:00
9144a18643 add overflow check before calling malloc()
It's preferable to be over-strict and paranoid here.

Change-Id: Ia928b279b753fa8f836d17decb17a35e6dc441b3
2012-06-20 23:58:43 -07:00
81720c9139 consistency cosmetics
Change-Id: Ie8e372ddcdd6e48527478f70bf716953ed18292a
2012-06-20 23:56:11 -07:00
2ebe8394f8 Merge "Add kramdown version information to README" 2012-06-20 16:02:03 -07:00
7144308402 enc/vp8l.c: fix build
broken since:
233a589 take picture->argb_stride into account for lossless coding

Change-Id: I9ecbbf65f3048be3077d28c3a20dfc0e1afa10be
2012-06-20 14:44:39 -07:00
b7ac19fec9 Add kramdown version information to README
Example kramdown command line to apply syntax highlighting requires
kramdown latest. Command is slightly different for earlier versions.

modified:   doc/README

Change-Id: Icda1197436b5c5ed936ceff53c1dc51aa4ce409b
2012-06-20 14:17:36 -07:00
efdcb6670c Merge "Edit for consistency, usage and grammar." 2012-06-20 14:01:13 -07:00
08220102d1 Enable alpha in vvwebp
Change-Id: I9499b6d4ea64b7eab18cb377f0bb09b6b494c534
2012-06-20 11:20:48 -07:00
8de9a0847b Merge "Mux API change:" 2012-06-20 11:11:49 -07:00
b74ed6e766 Mux API change:
'Set' and 'Get' methods for images take/return a bitstream as input,
instead of separate 'image' and 'alpha' arguments.

Also,
- Make WebPDataCopy() a public API
- Use WebPData for storing data in WebPChunk.
- Fix a potential memleak.

Change-Id: I4bf5ee6b39971384cb124b5b43921c27e9aabf3e
2012-06-20 11:00:42 -07:00
233a589ea9 take picture->argb_stride into account for lossless coding
analysis phase was assuming flat layout...

Change-Id: I96681f6d76aa3faabc51dd2ee3dffbe77ff90d36
2012-06-20 10:13:04 -07:00
04e33f17de Edit for consistency, usage and grammar.
Substantial edit, though less than 100% thorough.  This
makes changes that are clearly safe, but avoids others
where my domain knowledge is incomplete and accuracy
might be compromised.

modified:   doc/webp-lossless-bitstream-spec.txt

Change-Id: I89361a2e1157b8d2e44a8b4f4603f65833f0c1e6
2012-06-20 10:08:53 -07:00
a575b4bc15 Merge "cosmetics: add missing const" 2012-06-19 22:34:28 -07:00
8d99b0f4bd Merge "cosmetics: remove unimplemented function proto" 2012-06-19 22:33:54 -07:00
69d022176d cosmetics: add missing const
Change-Id: I0c0e89bbe826961b02a40ada5a6f89e02abee378
2012-06-19 22:28:34 -07:00
5b08318b57 cosmetics: remove unimplemented function proto
Change-Id: I580fcc756d161cebbabf753ee68c09d189628d7f
2012-06-19 22:27:56 -07:00
b7fb0ed567 Log warning for unsupported options for lossless.
Change-Id: I30d4e775959c7a4a8f5b649c8c90a4edb8cced47
2012-06-20 10:51:50 +05:30
e1f769fe1c msvc: add intrinsic based BitsLog2Floor
Change-Id: Ic0c7d2f03e300c6699e130916a759403e672f9d8
2012-06-19 16:14:54 -07:00
8a69c7d8af Bug-fix: Clamp backward dist to 1.
Check for valid bounds on the 'dist' in backward reference case.
Clamp it to 1 in case of zero and negative values.

Change-Id: I78e956d4595955efa02b1f9628b475093f6ee001
2012-06-19 16:44:03 +05:30
b5b6ac979f Merge "Bring the special writer 'WebPMemoryWriter' to public API" 2012-06-18 16:22:17 -07:00
a6a1909fff Merge "Fix floating point exception with cwebp -progress" 2012-06-18 16:11:53 -07:00
f2cee06708 Fix floating point exception with cwebp -progress
Adds a test of enc->mb_h_ to VP8IteratorProgress() to avoid a division
by zero. Fixes issue #121.

Original patch from even rouault (even dot rouault at gmail dot com).

Change-Id: Ie5fcc1821860c0a9366d5aa11f3aded4f5b98ed7
2012-06-18 15:43:28 -07:00
91b7a8c754 Bring the special writer 'WebPMemoryWriter' to public API
=> WebPMemoryWriter, WebPMemoryWriterInit(), WebPMemoryWrite()

Change-Id: I142fb22b0290ece7a6f6d74f00964a2e9e58ec9b
2012-06-18 15:42:56 -07:00
310e297205 support resize and crop for RGBA input
Change-Id: I19eac3fb4f8ecb973ff5872ac3a921f8947054bf
2012-06-18 15:20:46 -07:00
a89835d33a Merge changes Ice662960,Ie8d7aa90,I2d996d5e,I01c04772
* changes:
  Manually number "chapters," as chapter numbers are used in the narrative.
  Re-wrap at <= 72 columns
  Apply inline emphasis and monospacing, per gdoc / PDF
  Incorporate gdoc changes through 2012-06-08
2012-06-18 14:19:58 -07:00
ce614c0caf Merge "dec/vp8: avoid setting decoder status twice" 2012-06-18 14:12:21 -07:00
900285dac3 dec/vp8: avoid setting decoder status twice
Has the potential of returning confusing USER_ABORT for bitstream errors
in some cases.

Change-Id: I358fdb0b36549179df6dc2a95c09a872bd35aa24
2012-06-18 14:10:01 -07:00
8227adc878 Merge changes I6f02b0d0,I5cbc9c0a,I9dd9d4ed,Id684d2a1
* changes:
  Removed CodeRay syntax declarations ...
  Provide for code-block syntax highlighting.
  Replace high ASCII artifacts (curly quotes, etc.).
  Lossless WebP doc largely ported to markdown text.
2012-06-18 13:55:21 -07:00
dcda59c12d Merge "demux: rename SetTile to SelectTile" 2012-06-18 13:50:45 -07:00
622ef12ea5 demux: rename SetTile to SelectTile
Change-Id: I52b0b30578aeb77b71069f355c6b00901b948634
2012-06-18 13:49:41 -07:00
81ebd37505 Merge "demux: add {Next,Prev}Frame" 2012-06-18 13:41:42 -07:00
02dd37a2a3 demux: add {Next,Prev}Frame
Replaces WebPDemuxSetFrame().

Change-Id: I38cef0ebeabb5c2f164322502abe1780f7a65e06
2012-06-18 13:37:16 -07:00
4b79fa5923 Merge "Limit the maximum size of huffman Image to 16MB." 2012-06-17 00:20:26 -07:00
9aa34b3438 Manually number "chapters," as chapter numbers are used in the narrative.
modified:   doc/webp-lossless-bitstream-spec.txt

Change-Id: Ice662960ff988a6ff577a8ca5a594e14ba69d4de
2012-06-15 14:54:19 -07:00
2a4c6c29a0 Re-wrap at <= 72 columns
modified:   doc/webp-lossless-bitstream-spec.txt

Change-Id: Ie8d7aa907dc20d941b74455f8657d4e1b4e23bbb
2012-06-15 14:54:13 -07:00
a45adc1918 Apply inline emphasis and monospacing, per gdoc / PDF
modified:   doc/webp-lossless-bitstream-spec.txt

Change-Id: I2d996d5e80967641e22997d4f6e7173b39df0978
2012-06-15 14:54:06 -07:00
91011206ad Incorporate gdoc changes through 2012-06-08
modified:   doc/webp-lossless-bitstream-spec.txt

Change-Id: I01c04772bff7e3d7d6f0b526949cd846b5a97c8d
2012-06-15 14:53:55 -07:00
7a18248716 Removed CodeRay syntax declarations ...
... as they became unnecessary when upstream (kramdown)
implemented LQ feature request:
17625c8082

Also updated (and simplified) syntax-highlighting instructions.

modified:   doc/README
modified:   doc/webp-lossless-bitstream-spec.txt
Change-Id: I6f02b0d0a69a4d1d96cb0f771936cbe9e2e6bbec
2012-06-15 14:53:39 -07:00
b3ec18c556 Provide for code-block syntax highlighting.
modified:   doc/README
modified:   doc/webp-lossless-bitstream-spec.txt

Change-Id: I5cbc9c0a4fbbcc049a4d792e1fac367d28acf4a6
2012-06-15 14:53:29 -07:00
709d770241 Replace high ASCII artifacts (curly quotes, etc.).
modified:   doc/webp-lossless-bitstream-spec.txt
Change-Id: I9dd9d4ed05c8f93d4cafadf8c99cc21c300a9299
2012-06-15 14:53:16 -07:00
930e8abbda Lossless WebP doc largely ported to markdown text.
Word-level formatting (italics, bold) remains to be done,
but awaits final author edits, to avoid rework.

modified:   doc/template.html
new file:   doc/webp-lossless-bitstream-spec.txt

Change-Id: Id684d2a10d02e197d660a960540fe83f87d317f2
2012-06-15 14:52:57 -07:00
18cae37b91 msvc: silence some build warnings
these are related to the removal of USE_LOSSLESS_ENCODER

Change-Id: Ib0ca5bc2766c351130507bc2e657a034c8455b34
2012-06-13 13:51:50 -07:00
b3923084b9 Limit the maximum size of huffman Image to 16MB.
This limit corresponds to default histo_bits=3 for images upto sizes 400x400.
Any image higher than this dimension will bump up the histo_bits to 4 internally.

Change-Id: Ic8ba3dcd50e9c588cbbc4a0457289086498ff4ee
2012-06-13 13:27:43 +05:30
f180df2afd Merge "libwebp/demux: add Frame/Chunk iteration" 2012-06-13 00:29:29 -07:00
2bbe1c9aa0 Merge "Enable lossless encoder code" 2012-06-13 00:28:42 -07:00
d0601b0107 Merge changes I1d97a633,I81c59093
* changes:
  libwebp/demux: add WebPDemuxGetI
  libwebp/demux: add extended format parsing
2012-06-13 00:28:25 -07:00
78f3e34504 Enable lossless encoder code
Remove USE_LOSSLESS_ENCODER compile flag
Update Makefile.am and makefile.unix

Change-Id: If7080c4d8f37994c7c784730c5e547bb0a851455
2012-06-13 00:26:58 -07:00
d974a9cc15 Merge "libwebp/demux: add simple format parsing" 2012-06-13 00:03:37 -07:00
26bf223280 Merge "libwebp: add WebPDemux stub functions" 2012-06-12 23:57:18 -07:00
2f66668882 Merge "modify WebPParseHeaders to allow reuse by GetFeatures" 2012-06-12 23:51:55 -07:00
b402b1fbb9 libwebp/demux: add Frame/Chunk iteration
Change-Id: I2da68611b375de48391adcf446df31a93450c7d8
2012-06-12 23:45:11 -07:00
ad9ada3b9f libwebp/demux: add WebPDemuxGetI
Enables queries for format flag, canvas width/height.

Change-Id: I1d97a633712141e42dfc86c95492eb6da5cefa01
2012-06-12 23:45:10 -07:00
2f2d4d5889 libwebp/demux: add extended format parsing
Extends parser to support 'VP8X' and its components

Change-Id: I81c59093b02c7ad27810a7b0473129ea06f99952
2012-06-12 23:45:08 -07:00
962dcef6a8 libwebp/demux: add simple format parsing
Adds image parsing / validation framework for 'VP8 '/'VP8L' files

Change-Id: I8b0a5d1f20d86ab137c881a01dba3275ea191aa4
2012-06-12 23:45:06 -07:00
f8f94081be libwebp: add WebPDemux stub functions
beginning of a separate interface to demux webp files.

Change-Id: If8bf9c43defe5f6c8678afd03541f7cd8261c99a
2012-06-12 23:45:02 -07:00
fb47bb5cf4 Merge "NumNamedElements() should take an enum param." 2012-06-11 00:23:39 -07:00
7c6898051e Fix asserts in Palette and BackwardReference code.
Fix inequality assertion on number of palette colors.
Fix inequality assertion test in BackwardReferencesHashChainFollowChosenPath.

Change-Id: Ie3242f1bbeaf96db91b839b6732ccce2634cebf3
2012-06-11 12:27:59 +05:30
fbdcb7ea38 NumNamedElements() should take an enum param.
- Move TAG_ID to webp/mux.h
- Rename it to WebPChunkId
- Rename IDs to WEBP_CHUNK_<tag>
- Remove "name" param from ChunkInfo struct and related changes.
- Rename WebPMuxNumNamedElements to WebPMuxNumChunks().
- WebPMuxNumChunks() takes WebPChunkId as param.

Change-Id: Ic6546e4a9ab823b556cdbc600faa137076546a2b
2012-06-11 12:26:13 +05:30
fb4943bdf9 modify WebPParseHeaders to allow reuse by GetFeatures
moves the implementation to ParseHeadersInternal. this also allows
decoding to start at a VP8X sub-chunk, e.g. 'ALPH'.

Change-Id: I06791f87d90f888de32746ecb02705e4b0ff227a
2012-06-08 14:55:14 -07:00
3697b5ceb2 write an ad-hoc EncodeImageInternal variant
Used when we don't code a Huffman Image.
-> Simplify the code quite some because we don't have to
deal with special cases of histo bits

Change-Id: I0c3f46cbf3b501e021c093e07253e7404c01ff4f
2012-06-08 11:52:31 -07:00
eaee9e79f7 Bug-Fix: Decode small (less than 32 bytes) images.
ParseVP8X was checking for presence of extra 20 bytes (after RIFF header).
This check should not be executed for non-mux (non-VP8X) images.

Change-Id: I3fc89fa098ac0a53102e7bbf6c291269817c8e47
2012-06-08 14:30:56 +05:30
0bceae488e Merge "cwebp: fix alpha reporting in stats output" 2012-06-07 21:27:02 -07:00
0424b1ef3b Rebase default encoding settings.
Updated histo_bits to 3 from 4 and changed the quality threshold for inner loop for HashChainFindCopy.
Impact: 0.5%-0.8% better bpp with 15%-20% hit on encoding throughput
at default encoding settings.

Change-Id: I316ef88403148b1e19036fa0817d944eb0301255
2012-06-08 09:18:15 +05:30
c71ff9e359 cwebp: fix alpha reporting in stats output
Since
 437999f introduce a generic WebPPictureHasTransparency() function
lossy encodes will not encode alpha if the alpha channel is completely
opaque.

Change-Id: I1826669c3932483650d7f8ce806cfebd4e5225fc
2012-06-07 17:32:29 -07:00
e2ffe446bd Merge "Stop indefinite recursion for Huffman Image." 2012-06-07 10:52:26 -07:00
70eb2bd687 Stop indefinite recursion for Huffman Image.
Ensure that the lossless bit-stream doesn't allow for such cases and
safe-gaurd decoder against indefinite recursion.

Change-Id: Ia6d7f519291de8739f79a977a5800982872aae71
2012-06-07 17:27:19 +05:30
f3bab8eb27 Update vwebp
... with updated mux API calls.

Change-Id: I0f8a36eeffaeb041964ec4f582e369d48bba4978
2012-06-07 17:20:26 +05:30
6d5c797cee Remove support for partial files in Mux.
Change-Id: Ie084f308c79a3cfaf2ea13e800397debc2643470
2012-06-07 13:46:42 +05:30
f1df5587d9 WebPMuxAssemble() returns WebPData*.
Also add an API 'WebPDataFree()'

Change-Id: I00dc4c67fd78a541a18aaf0e65b8ef62d9769803
2012-06-07 11:05:57 +05:30
814a063925 Rename 'Add' APIs to 'Set'.
Change-Id: I10a836a5bc3c9207b6f7fa423bb64bc9bcac055b
2012-06-05 20:37:51 +05:30
bbb0218fa5 Update Mux psuedo-code examples.
Change-Id: I9df4d509286c9b892272181646403c0c7893db70
2012-06-05 20:29:42 +05:30
4fc4a47f6e Use WebPData in MUX set APIs
Change-Id: Ibdede3c1cd02c6aeef333718592da313f10f6408
2012-06-05 14:21:46 +05:30
c67bc979dd Merge "add WebPPictureImportRGBX() and WebPPictureImportBGRX()" 2012-06-05 00:28:04 -07:00
27519bc2b6 add WebPPictureImportRGBX() and WebPPictureImportBGRX()
When importing BGRA or RGBA data for encoding, provide variants of
the WEBPImportPicture API for RGBX and BRGX data meaning the alpha
channel should be ignored.

Author: noel@chromium.org
from Chromium patch: https://chromiumcodereview.appspot.com/10496016/

Change-Id: I15fcaa4160c69a2b5549394204b6e6d7a1c5d333
2012-06-05 00:26:17 -07:00
f80cd27e28 factorize code in Import()
Change-Id: I6a4e77715c3a5be5238c81e029bfb6479d36de05
2012-06-04 23:27:01 -07:00
9b71502669 histogram: add log2 wrapper
Change-Id: I5e68efaf5f763a42ace1af83f4b7887e0ddfd099
2012-06-04 23:14:41 -07:00
8c34378ff3 Merge "fix some implicit type conversion warnings" 2012-06-04 22:46:33 -07:00
42f6df9da3 fix some implicit type conversion warnings
Change-Id: I0653d10410c0d46f91fedad4c4dffa9c1de402cb
2012-06-04 22:33:32 -07:00
250c16e3d5 Merge "doc: update lossless pdf" 2012-06-04 22:30:41 -07:00
9d9daba43d Merge "add a PDF of the lossless spec" 2012-06-04 22:30:19 -07:00
8fbb91884e prefer webp/types.h over stdint.h
stdint.h is part of C99 and is notably lacking under MSVC

Change-Id: Iff60dcb8bdcc7f948dc35fb0b5d47478520b570f
2012-06-04 18:34:24 -07:00
0ca170c2dd doc: update lossless pdf
Change-Id: I8c6d0e6fc21196f6dd239b21eda2097ba4ce2bba
2012-06-04 16:53:16 -07:00
0862ac6e7e add a PDF of the lossless spec
(cherry picked from commit db9967748781d73832b3875db1dfcbefb180f7c9)

Change-Id: I5909abc48d9b4c4415f81a019681968f06d6a3d7
2012-06-04 16:51:39 -07:00
437999fb77 introduce a generic WebPPictureHasTransparency() function
VP8-lossy will now avoid writing an ALPH chunk if the
alpha values are trivial.

+ changed DumpPicture() accordingly in cwebp
+ prevented the -d option to be active with lossless
 (DumpPicture wouldn't work).

Change-Id: I34fdb108a2b6207e93fa6cd00b1d2509a8e1dc4b
2012-06-04 16:17:55 -07:00
d2b6c6c03b cosmetic fixes after Idaba281a
Change-Id: I275a3dee5696fe1a3e2db0976f8241f2044be512
2012-06-04 13:19:28 -07:00
b4e6645c61 Merge "add colorspace for premultiplied alpha" 2012-06-04 07:56:37 -07:00
48f827574e add colorspace for premultiplied alpha
The new modes are
       MODE_rgbA
       MODE_bgrA
       MODE_Argb
       MODE_rgbA_4444
It's binary incompatible, since the enums changed.

While at it, i removed the now unneeded KeepAlpha methods.
-> Saved ~12k of code!

* made explicit mention that alpha_plane is persistent,
so we have access to the full alpha plane data at all time.
Incremental decoding of alpha was planned for, but not
implemented. So better not dragged this constaint for now
and make the code easier until we revisit that.

Change-Id: Idaba281a6ca819965ca062d1c23329f36d90c7ff
2012-06-04 07:50:41 -07:00
069f903a08 Change in lossless bit-stream.
Change the lossless signature to 0x2f
Add 1 bit indicator for 'droppable (or trivial) alpha)'.
Add 3 bit lossless version (for future extension like yuv support).
Change the sub-resolution information to 3 bits implying range [2 .. 9]

Change-Id: Ic7b8c069240bbcd326cf5d5d4cd2dde8667851e2
2012-06-04 12:47:01 +05:30
5f7bb3f53a Merge "WebPReportProgress: use non-encoder specific params" 2012-05-31 13:02:30 -07:00
f18281ffa0 WebPReportProgress: use non-encoder specific params
Take picture and percent value storage location instead of VP8Encoder.
This will allow reuse by the lossless encoder.

Change-Id: Ic49dbc800cc3e2df60d20f4ebac277f68ed6031b
2012-05-31 11:28:48 -07:00
9ef3228301 Add support for raw lossless bitstream in decoder.
Previously, it used to assume any raw bitstream is a VP8 one.

Also,
- Factor out VP8CheckSignature() & VP8LCheckSignature().
- Use a local var for *data_ptr in ParseVP8Header() for
readability.

Change-Id: I0fa8aa177dad7865e00c8898f7e7ce76a9db19d5
2012-05-31 14:03:17 +05:30
7cbee29afa Fix bug: InitIo reseting fancy_upsampling flag.
frame's InitIo should not reset fancy_upsampling flag.
This flag (fancy_upsampling) is set via CustomSetup -> WebPIoInitFromOptions
and frame's InitIo() is resetting it to 0.

Change-Id: I64b54cdfba43799c0a5aa8e384575af5d6331674
2012-05-30 14:46:22 +05:30
880fd98ca1 vwebp: fix exit w/freeglut
GLUT_ACTION_CONTINUE_EXECUTION is required to allow glutMainLoop to
return.

Change-Id: I8eb7e657a52d6923858959b7b03447a7ddb53ca0
2012-05-25 15:06:21 -07:00
1875d926e7 trap two unchecked error conditions
CostModelBuild() and TrackBackwards() returns weren't checked

+ code clean-up
+ de-inline VP8LBackwardRefs non-critical methods
+ shuffle the .h around to group things together
+ extract some constants as #define's
+ fixed the "if (!(cc_init = ...)) {...}" constructs
+ removed some unneeded VP8L prefixes

Change-Id: Ic634cb87bc6b2033242d3e8e8731fab4c134f327
2012-05-25 02:52:44 -07:00
87b4a908a5 no need to have mux.h as noinst clause in enc/
Change-Id: I93c65838f33d01170fc63340650edeeee753786f
2012-05-24 15:05:19 -07:00
88f41ec6ec doc: fix bit alignment in VP8X chunk
Change-Id: I7eaa7be48213642e3eceaaac95ad00952e085330
2012-05-24 12:33:48 -07:00
52f5a4eff0 Merge "fix bug with lossy-alpha output stride" 2012-05-24 08:56:44 -07:00
3bde22d768 fix bug with lossy-alpha output stride
dec->width_ != final_width in case of Bundle transform!

thanks to Pepijn for spotting the problem

Change-Id: I08b451a32097dcbf23b73deabc8cc6a2d59f0119
2012-05-24 08:54:02 -07:00
42d61b6def update the spec for the lossy-alpha compression methods.
No further experiments are to be expected, so this is quite the
final format so far, pending supplemental feedbacks.

Change-Id: I2a3de025c90b7bb5fdd8792b2b2ccdc2e3753f56
2012-05-24 07:22:52 -07:00
e75dc80516 Move some more defines to format_constants.h
Also remove some duplicate const/defines.

Change-Id: I0ec48866b874f546022d72e938fb65669b0b3211
2012-05-24 17:46:01 +05:30
c13f663261 Move consts to internal header format_constants.h
Change-Id: Ic6180c16d0b4245680738992925e4608c593fbe8
2012-05-24 15:02:02 +05:30
7f2dfc92dc use a bit-set transforms_seen_ instead of looping
may be useful later for instance to bypass some code
if we know we don't use the Bundle+ColorMap transform.

Change-Id: I9dc70d18165b2363ad9ede763684ef3d8eba5903
2012-05-24 02:04:23 -07:00
18da1f53fc modulate alpha-compression effort according to config.method
we vary linearly lossless-method between 0 and 6,
and lossless-quality between 50 and 100, so that encoding
speed can go from 'quite fast' to 'rather slow'.
Impact on size is moderate, but visible.

Change-Id: I0b7917e7170eb50258afb1a4e248028cd9e9207d
2012-05-24 01:54:12 -07:00
f5f2fff657 Merge "Alpha flag fix for lossless." 2012-05-24 01:11:07 -07:00
c975c44ea5 Alpha flag fix for lossless.
- Make sure alpha flag is set in case of a lossless file with VP8X chunk.
  The semantic of ALPHA_FLAG changes with this: it means the images
  contain alpha (rather than ALPH chunk in particular).
- Update the mux container spec to add 1-line description of alpha
  flag.
- Rename "HasLosslessImages()" to "MuxHasLosslessImages()", and other
  similar function renames.
- Rename FeatureFlags to WebPFeatureFlags
- Elaborated a comment for a special case.
- A misc comment fix.

Change-Id: If212ccf4338c125b4c71c10bf281a51b3ba7ff45
2012-05-24 11:35:12 +05:30
4f067fb254 Merge "Android: only build dec_neon with NEON support" 2012-05-23 22:26:03 -07:00
255c66b48f Android: only build dec_neon with NEON support
Defining LOCAL_ARM_NEON = true can result in neon instructions being
used in portions unprotected by the cpu check.
This changes defines a WEBP_USE_NEON/WEBP_ANDROID_NEON pair similar to
the SSE2 code and MSVC.

Change-Id: Ifac010b06e42c73d5aca529baa2198c6796674bd
2012-05-23 22:21:10 -07:00
8f9117a9f0 cosmetics: signature fixes
Change-Id: Id0e1026d43c0a6928dd740c88117df638bfb6db6
2012-05-23 12:45:05 -07:00
39bf5d6497 use header-less lossless bitstream for alpha channel
This saves ~26 bytes of headers.
* introduce new VP8LDecodeAlphaImageStream() for decoding
* use VP8LEncodeStream() for encoding
* refactor code a bit

still TODO: make the alpha-quality/enc-method user-configurable

Change-Id: I23e599bebe335cfb5868e746e076c3358ef12e71
2012-05-23 08:01:44 -07:00
75d7f3b222 Merge "make input data be 'const' for VP8LInverseTransform()" 2012-05-23 07:54:12 -07:00
9a721c6d24 make input data be 'const' for VP8LInverseTransform()
Change-Id: I5b5b1e29bca6c42704df141b21632a0d0fcb07cf
2012-05-23 07:21:53 -07:00
9fc64edc21 Disallow re-use of same transformation.
Limit the overall number of transformations to 4 and disallow any
duplicate transform for decoding an image.

Change-Id: Ic4b0ecd553db96702e117fd073617237d95e45c0
2012-05-23 17:04:57 +05:30
98ec717f1e use a function pointer for ProcessRows()
this allows later customization of data output method.
No perf diff observed, even if ProcessRows is no longer inlined.

Change-Id: I6933a3612a9cf6c108cf2776dfde0ae80c6c07c0
2012-05-23 02:00:14 -07:00
f7ae5e370a cosmetics: join line
Change-Id: Ib27ed202fff439b94431360c8b0654d88962fb9a
2012-05-23 01:56:29 -07:00
140b89a323 factor out buffer alloc in AllocateARGBBuffers()
+ small opportunistic fixes:
  * allow NULL decoded_data to be passed to DecodeStream
    and clarity (with assert()) when to do so
  * AllocateAndInitRescaler() was already setting error status,
    as it should. No need to do it at caller's site

Change-Id: I30867e596564a7f459a0d1ddbf6f5d312414b7fd
2012-05-23 01:53:57 -07:00
a107dfa806 Rectify WebPParseOptionalChunks().
Now it stops at either VP8/VP8L chunk.

Change-Id: Iadac4fa47396b61b9b720b8b7b19138c89df24cc
2012-05-23 13:59:52 +05:30
237eab6764 Add two more color-spaces for lossless decoding.
Added color-spaces (RGBA_4444 and RGB_565), required for Android device
to lossless decoding.

Change-Id: I229832edd4deca59e066f463e7454f77457c5bcd
2012-05-23 12:10:13 +05:30
27f417ab66 fix orthographic typo
Change-Id: I4f62e9c125ef3bc1ab75871e6725551f1c89f5e8
2012-05-22 13:20:57 -07:00
489ec335a1 add VP8LEncodeStream() to compress lossless image stream
* RIFF header is omitted
* rename NewVP8LEncoder and DeleteVP8Encoder
* change the signature to take a "const WebPPicture*"
  (it was non-const just because we were setting some error potentially)
* made the pic_ field const in VP8LEncoder too.
* trap the bitwriter::error_ too
* simplify some signatures to take WebPPicture* instead
  of unneeded VP8LEncoder*

VP8LEncodeStream() will be called directly to compress alpha channel
header-less.

Change-Id: Ibceef63d2b3fbc412f0dffc38dc05c2dee6b6bbf
2012-05-22 03:15:58 -07:00
fa8bc3dbca make WebPEncodingSetError() take a const picture
This is a border-case situation: the picture is not const, because
we're change its error status. But taking it non-const forces
the caller to carry a non-const picture all around the code just
in case (0.00001% of the time?) something bad happen.
This pretty much the same as making all objects non-const because
we'll eventually call delete or free() on them, which is quite a
non-const operation. Well... Better allow constness enforcement for
the remaining 99.9999% of the code.

Change-Id: I9b93892a189a50feaec1a3a518ebf488eb6ff22f
2012-05-22 02:51:38 -07:00
638528cd1e bitstream update for lossy alpha compression
now, we only use 2 bits for the filtering method, and 2 bits
for the compression method.
There's two additional bits which are INFORMATIVE, to specify
whether the source has been pre-processed (level reduction)
during compression. This can be used at decompression time
for some post-processing (see DequantizeLevels()).

New relevant spec excerpt:

     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                      ChunkHeader('ALPH')                      |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |Rsv| P | F | C |     Alpha Bitstream...                        |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Compression method (C): 2 bits

: The compression method used:

  * `0`: No compression.
  * `1`: Backward reference counts encoded with arithmetic encoder.

Filtering method (F): 2 bits

: The filtering method used:

  * `0`: None.
  * `1`: Horizontal filter.
  * `2`: Vertical filter.
  * `3`: Gradient filter.

Pre-processing (P): 2 bits

: These INFORMATIVE bits are used to signal the pre-processing that has
been performed during compression. The decoder can use this information to
e.g. dither the values or smooth the gradients prior to display.

  * `0`: no pre-processing
  * `1`: level reduction

Decoders are not required to use this information in any specified way.

Reserved (Rsv): 2 bits

: SHOULD be `0`.

Alpha bitstream: _Chunk Size_ - `1` bytes

: Encoded alpha bitstream.

This optional chunk contains encoded alpha data for a single tile.
Either **ALL or NONE** of the tiles must contain this chunk.

The alpha channel data is losslessly stored as raw data (when
compression method is '0') or compressed using the lossless format
(when the compression method is '1').

Change-Id: Ied8f5fb922707a953e6a2b601c69c73e552dda6b
2012-05-22 02:36:22 -07:00
d73e63a726 add DequantizeLevels() placeholder
will be called by alpha post-processing, although doing nothing for now.
Gradient smoothing would be nice-to-have here. Patch welcome!

Change-Id: I534cde866bdc75da22d0f0a6d1373c90e21366f3
2012-05-22 02:28:19 -07:00
ec122e0986 remove arch-dependent rand()
let's use our own simple pseudo-random number generator

Change-Id: I7b4a190cdf4d338b1fae5ef3622ebd82c6e19274
2012-05-22 00:17:04 -07:00
d40e7653db fix alignment
Change-Id: Ieb36de1bcf8c624024f8a3d5a238a2b508d4bb51
2012-05-21 14:31:16 -07:00
1dd6a8b65e Merge "remove tcoder, switch alpha-plane compression to lossless" 2012-05-21 06:38:45 -07:00
3e863dda61 remove tcoder, switch alpha-plane compression to lossless
* Method #1 is now calling the lossless encoder on the alpha plane.
Format is not final, it's just a first draft. We need ad-hoc functions.
* removed now useless utils/alpha.*
* added utils/quant_levels.h instead
* removed the TCoder code altogether

Change-Id: I636840b6129a43171b74860e0a0fc5bb1bcffc6a
2012-05-21 06:24:48 -07:00
8d77dc29e1 Add support for lossless in mux:
- Separate out 'CHUNK_INDEX' from 'TAG_ID' (this is to help with the
  situation where two different tags - "VP8 " and "VP8L" can have the
  same TAG_ID -> IMAGE_ID).
- Some internal methods now take 'CHUNK_INDEX' param instea of 'TAG_ID'
  as appropriate.
- Add kChunks[] entry for lossless.
- Rename WebPMuxImage.vp8_ --> WebPMuxImage.img_
- SetImage() and AddFrame/Tile() infer whether the bitstream is a
  lossless one based on LOSSLESS_MAGIC_BYTE. The correct tag is stored
  based on this.

Also, handle the case when GetVP8Info/GetVP8LInfo() fails.

Change-Id: I6b3bc9555cedb791b43f743b5a7770958864bb05
2012-05-21 13:54:18 +05:30
831bd13168 Make tile size a function of encoding method.
Higher method implies more encoding effort (CPU) achieved with
smaller tile sizes (lower histo_bits).

Change-Id: Ic39c8d882c87835f74fde41172afb002ac3fd1c3
2012-05-18 12:16:39 +05:30
778c52284b Merge "remove some variable shadowing" 2012-05-17 14:03:10 -07:00
817c9dce61 Few more HuffmanTreeToken conversions.
Change-Id: I932b5368d279f83c462c7d916978dab3e81d7709
2012-05-16 12:15:52 +05:30
37a77a6bf4 remove some variable shadowing
Change-Id: I4348253ec6b50639095b22c4745dc26da0904466
2012-05-15 14:04:24 -07:00
89c07c9660 Merge "normalize example header includes" 2012-05-15 13:50:27 -07:00
4aff411fe0 Merge "add example_util.[hc]" 2012-05-15 13:49:21 -07:00
00b29e282f normalize example header includes
headers from examples/ aren't installed so prefix with './' to be
explicit.

Change-Id: Ie6c1544a026a9859b7fc0cf238b21e031ef0013f
2012-05-15 13:48:11 -07:00
061263a787 add example_util.[hc]
moves ReadFile to a common location

Change-Id: Ia81230671f16d7d4d218b9954a5be55577a85413
2012-05-15 13:42:57 -07:00
c6882c49e3 merge all tree processing into a single VP8LProcessTree()
-> 0.1% size improvement because we're calling OptimizeForRLE()
systematically now.

Change-Id: I03bd712175728e0d46323f375134cae5a241db4b
2012-05-14 05:49:02 -07:00
9c7a3cf5e7 fix VP8LHistogramNumCodes to handle the case palette_code_bits == 0
-> lot of simplifications ensue and we should be able to get rid of
 ClearHuffmanTreeIfOnlyOneSymbol() too, in a subsequent patch.

Change-Id: Ic4c51d05e4b1970e37f94ffd85fae6a02e4a6422
2012-05-14 01:23:58 -07:00
b5551d2e1d Merge "Added HuffmanTreeCode Struct for tree codes." 2012-05-14 01:23:08 -07:00
8b85d01c45 Added HuffmanTreeCode Struct for tree codes.
To represent tree codes (depth and bits array).

Change-Id: I87650886384dd10d95b16ab808dfd3bb573172bc
2012-05-14 13:50:35 +05:30
093f76d831 Merge "Allocate single memory in GetHuffBitLengthsAndCodes." 2012-05-14 01:15:46 -07:00
41d8049451 Allocate single memory in GetHuffBitLengthsAndCodes.
Allocate big chunk of memory in GetHuffBitLengthsAndCodes, instead of allocating in a loop.
Also fixed the potential memleak.

Change-Id: Idc23ffa306f76100217304444191a8d2fef9c44a
2012-05-14 13:41:59 +05:30
1b04f6d234 Correct size in VP8L header.
The size written in VP8L header should be without padding.
(Also clarified this code using consts).

Change-Id: Ic6583d760c0f52ef61924ab0330c65c668a12fdc
2012-05-14 12:49:16 +05:30
2924a5aee6 Makefile.vc: split object lists based on directory
Change-Id: I4e189193817f26117e6459da078e2a72c9f3ef5f
2012-05-11 18:29:33 -07:00
c8f24165b7 Merge "add assert(tokens)" 2012-05-11 01:46:45 -07:00
432399472f add assert(tokens)
Change-Id: I952a5cd15ff0a80cff349293e6403357cbc7bd8d
2012-05-11 01:45:06 -07:00
9f547450e0 Catch an error in DecodeImageData().
When we are at end-of-stream, but haven't decoded all pixels, we should
return an error.
Also remove an obsolete TODO.

Change-Id: I3fb1646136e706da536d537a54d1fa487a890630
2012-05-11 14:10:01 +05:30
ac8e5e42d1 minor typo and style fix
Change-Id: If4927beb7a8f3c96379eee1fedc687a5046a6951
2012-05-11 01:17:31 -07:00
9f566d1d36 clean-up around Huffman-encode
* refine doc
* use LUT for bit-reversal
* added assert
* Introduce HuffmanTreeToken instead of separate code/extra_bits arrays

Change-Id: I0fe0b50b55eb43a4be9f730b1abe40632a6fa7f0
2012-05-10 09:11:47 -07:00
c579a71012 Introduce CHUNK_SIZE_BYTES in muxi.h.
Plus a style fix.

Change-Id: Id94df6c91a96598cb022c813e0981f542aebe982
2012-05-10 13:16:18 +05:30
14757f8ae2 Make sure huffman trees always have valid symbols
- Symbols added to the tree are valid inside HuffmanTreeBuildExplicit().
- In HuffmanTreeBuildImplicit(), make sure 'root_symbol' is
valid in case of a single symbol tree.

Change-Id: I7de5de71ff28f41e2d6228b29ed8dd4a20813e99
2012-05-10 11:40:18 +05:30
4105061840 makefile.unix: add support for building vwebp
standalone from the normal examples due to the additional OpenGL
dependencies.
$ make -f makefile.unix examples/vwebp

Change-Id: I7e3f0b5e0328230cb32acdd1e2a011cbbb80e55d
2012-05-09 15:47:00 -07:00
48b37721fc Merge "fixed signed/unsigned comparison warning" 2012-05-09 14:10:32 -07:00
57f696daef Merge "EncodeImageInternal: fix potential leak" 2012-05-09 13:58:55 -07:00
d972cdf2dd EncodeImageInternal: fix potential leak
if histogram_image_size is reduced in when writing the histogram_image
the bit arrays would leak any remaining elements. store their element
count separately.

Change-Id: I710142a11ebd4325faec7bd65c2d2572aae19307
2012-05-09 13:49:10 -07:00
5cd12c3df2 fixed signed/unsigned comparison warning
present in an assert in backward_references.h

Change-Id: Id9a528896689e51d784ad64fdc1ca052e97fe7a2
2012-05-09 13:45:26 -07:00
cdca30d0b5 Merge "cosmetics: shorten long line" 2012-05-09 13:45:12 -07:00
e025fb5546 cosmetics: shorten long line
Change-Id: I78bb19cf593730e74734e08d3d6de08a29bd9cbc
2012-05-09 13:42:31 -07:00
22671ed6a3 Merge "enc/vp8l: fix double free on error" 2012-05-09 13:36:38 -07:00
e1b9b05258 Merge "cosmetics: VP8LCreateHuffmanTree: fix indent" 2012-05-09 13:23:24 -07:00
a8e725f800 enc/vp8l: fix double free on error
GetHuffBitLengthsAndCodes and the caller would both free
bit_lengths/bit_codes

Change-Id: I1b62ff727c82567f11c39a847f74fe765b5f527c
2012-05-09 12:56:07 -07:00
27541fbdc0 cosmetics: VP8LCreateHuffmanTree: fix indent
Change-Id: I025fada400366f8937208e1f9b8df483cd53a942
2012-05-09 12:11:22 -07:00
1d38b258b8 cwebp/windows: use MAKE_REFGUID where appropriate
Change-Id: If595c75a833d37df86c16bf926da5f032015c68e
2012-05-09 11:52:01 -07:00
817ef6e9af Merge "cwebp: fix WIC/Microsoft SDK compatibility issue" 2012-05-09 11:50:05 -07:00
902d3e3b5f cwebp: fix WIC/Microsoft SDK compatibility issue
Fixes issue #115.
Define local copies of GUID_WICPixelFormat24bppRGB &
GUID_WICPixelFormat32bppRGBA (and GUID_WICPixelFormat32bppBGRA for
symmetry) to avoid link issues when building against older versions of
the SDK.

Change-Id: I2a26be1b7fe6d970feb3211cf0059e5898e3028d
2012-05-09 11:47:15 -07:00
89d803c423 Merge "Fix a crash due to wrong pointer-integer arithmetic." 2012-05-09 02:22:01 -07:00
cb1bd741f9 Merge "Fix a crash in lossless decoder." 2012-05-09 02:21:13 -07:00
de2fe20290 Merge "Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool' outside the loop. - Some renames/tweaks for readability." 2012-05-09 02:15:42 -07:00
ce69177a41 Fix a crash due to wrong pointer-integer arithmetic.
[Basically, the condition "src - dist < data" can be wrongly evaluated
to be false if "src < dist" due to underflow. Instead, "src - data <
dist" is the correct condition, as "src > data" is always true and so
there would never be an underflow].

Change-Id: Ic9f64bfe76a9acae97abc1fb7c1f4868e81f1eb8
2012-05-09 14:40:06 +05:30
e40a3684f5 Fix a crash in lossless decoder.
This was due to incorrect update of 'ok'.

Change-Id: I91edfb5682527e8bc7eaa10e635c0261edb016b6
2012-05-09 14:35:11 +05:30
3927ff3abc remove unneeded error condition for WebPMuxNumNamedElements()
+ fix error message

Change-Id: If283ff6609ee0c5dff81a9c1c3f194faa729161c
2012-05-09 01:57:15 -07:00
2c140e113c Some cleanup in VP8LCreateHuffmanTree() (and related functions
CompareHuffmanTrees() and SetBitDepths()):
- Move 'tree_size' initialization and malloc for 'tree + tree_pool'
  outside the loop.
- Some renames/tweaks for readability.

Change-Id: I5cb3cc942afac6e9f51a0b97c57ee897677a48a2
2012-05-09 14:26:01 +05:30
861a5b7bc9 add support for animation
it's using glutTimerFunc() for trigger next frame's decoding

Reminder: how to build and use manually (integration in makefile.unix coming soon)

Unix:
cd libwebp
make -f makefile.unix
cd examples
cc -o vwebp vwebp.c -framework GLUT -framework OpenGL -I../src/ ../src/libwebp.a ../src/mux/libwebpmux.a

Mac + xcode:
cd libwebp
make -f makefile.unix
cd examples
cc -o vwebp vwebp.c -lglut -lGL -lpthread -lm -I../src/ ../src/libwebp.a ../src/mux/libwebpmux.a

Change-Id: Id49e87c26628c2b58ec221f6030cbd2e46a807b4
2012-05-09 01:23:39 -07:00
eb5c16cc18 Merge "Set correct encode size in encoder's stats." 2012-05-09 00:37:25 -07:00
4abe04a204 fix the return value and handle missing input file case.
Change-Id: I42a64fcf7587ff3a487115808e2cfa51cec0031d
2012-05-09 00:34:30 -07:00
2fafb85579 Set correct encode size in encoder's stats.
The current implementation doesn't take care one byte
signature and associated one byte padding (for odd sized chunk).

Change-Id: I35b81d0644818cdba38189aa48c75db5f92e68f4
2012-05-09 12:33:58 +05:30
e7167a2b95 Provide one entry point for backward references.
The new method VP8LGetBackwardReferences hides internal
heuristics used for choosing RLE or LZ77 based refs.
- Tuned VP8LHashChainFindCopy for better compression at higher Q.
- Refactored code.
- Removed the unused method VP8LVerifyBackwardReferences.

Change-Id: Ibb7bb072bab5a49a001577a20d88226f52e6c663
2012-05-09 12:13:06 +05:30
c4ccab6463 Print relevant lossless encoding stats in cwebp.
Change-Id: Ib8dfab498b1a05e49734bba2d3e72810343e8b0a
2012-05-09 11:31:41 +05:30
e3302cfd73 GetHuffBitLengthsAndCodes: reduce level of indirection
arrays can be passed directly as only their members are being modified.
this also reduces the allocation for bit_codes[] by taking the
sizeof(type)=2 rather than sizeof(ptr)=4/8 in one case.

Change-Id: Idad20cead58c218b58d90b71699374fefd01cad9
2012-05-08 11:48:55 -07:00
b5f2a9ed49 enc/vp8l: fix uninitialized variable warning
histogram_image_size could be unset if the code branches to the 'Error'
label.

Change-Id: I690b20f86372f19a47d159c2bec8fbf49553f0d5
2012-05-08 11:45:04 -07:00
7885f8b25d makefile.unix: add lossless encoder files
comment out 'EXTRA_FLAGS += -DUSE_LOSSLESS_ENCODER' to disable

Change-Id: I75ead699c95644e319e7cbbf2a167d98e826fb7c
2012-05-08 11:25:28 -07:00
1261a4c888 Merge "cosmetics" 2012-05-07 17:54:43 -07:00
3926b5be3b Merge "dsp/cpu.c: Android: fix crash on non-neon arm builds" 2012-05-07 17:53:03 -07:00
834f937f3c dsp/cpu.c: Android: fix crash on non-neon arm builds
add proper cpu-detection for Android targets

Fixes issue #118 (and is a better solution for #117).

based on patch by pepijn vaneeckhoudt

Change-Id: I6b00ea6d51ca658ccf6a3d55b87b99c01c6805be
2012-05-07 17:52:15 -07:00
126e160672 cosmetics
vp8.[hc]:fix '*' placement in pointer types
vp8l.c: remove trailing ',' from enum
decode.h: fix stray WebPINew()

Change-Id: Id6749a14a12ed0a090649f28ef4267fda45a37a8
2012-05-07 17:47:51 -07:00
e38602d2ad Merge branch 'lossless_encoder'
* lossless_encoder: (46 commits)
  split StoreHuffmanCode() into smaller functions
  more consolidation: introduce VP8LHistogramSet
  big code clean-up and refactoring and optimization
  Some cosmetics in histogram.c
  Approximate FastLog between value range [256, 8192]
  Forgot to update out_bit_costs to symbol_bit_costs at one instance.
  Evaluate output cluster's bit_costs once in HistogramRefine.
  Simple Huffman code changes.
  Lossless decoder: remove an unneeded param in ReadHuffmanCodeLengths().
  Reducing emerging palette size from 11 to 9 bits.
  Move GetHistImageSymbols to histogram.c
  Improve predict vs no-predict heuristic.
  code-moving and clean-up
  reduce memory usage by allocating only one histo
  Restrict histo_bits to ensure histo_image size is under 32MB
  further simplification for the meta-Huffman coding
  A quick pass of cleanup in backward reference code
  Make transform bits a function of encode method (-m).
  introduce -lossless option, protected by USE_LOSSLESS_ENCODER
  Run TraceBackwards for higher qualities.
  ...

Conflicts:
	src/enc/webpenc.c

Change-Id: I9a5d98cba0889ea91d10699466939cc283da345a
2012-05-07 14:27:17 -07:00
e8d3d6a018 split StoreHuffmanCode() into smaller functions
Change-Id: Iaa715f4997505eebabee1e92e964a5d7ee6f3e7d
2012-05-07 14:25:32 -07:00
d0d88990d8 more consolidation: introduce VP8LHistogramSet
VP8LHistogramSet is container for pointers to histograms that
we can shuffle around. Allocation is one big chunk of memory.
Downside is that we don't de-allocate memory on-the-go during
HistogramRefine().

+ renamed HistogramRefine() into HistogramRemap(), so we don't
confuse with "HistogramCombine"
+ made VP8LHistogramClear() static.

Change-Id: Idf1a748a871c3b942cca5c8050072ccd82c7511d
2012-05-07 14:25:30 -07:00
1a210ef1a9 big code clean-up and refactoring and optimization
* de-inline some function
* make VP8LBackwardRefs be more like a vectorwith max capacity
* add bit_cost_ field to VP8LHistogram
* general code simplifications
* remove some memmov() from HistogramRefine
* simplify HistogramDistance()
...

Change-Id: I16904d9fa2380e1cf4a3fdddf56ed1fcadfa25dc
2012-05-07 14:25:29 -07:00
41b5c8ff71 Some cosmetics in histogram.c
Change-Id: I5d5872a793759fad593dba88c3f593f72b328b0c
2012-05-07 14:25:28 -07:00
ada6ff77df Approximate FastLog between value range [256, 8192]
Profiled data: Profiled few images and found that in the function VP8LFastLog,
90% of time table lookup is performed, while rest of time (10%) call to log
function is made. Typical lookup accounts for 10 CPU instructions and call to
log 200 instruction counts. The weighted average comes out to be 30
instructions per call. For mid qualities (25-75), this function (VP8LFastLog)
accounts for 30-50% of total CPU cycles (via call path: VP8LCOlorSpaceTransform
-> PredictionCostCrossColor -> ShannonEntropy). After this change, the log is
called less that 1% of time, with average instructions being 15 per call.
Measured the performance over 1000 files for various qualities and found
overall compression speedup between 10-15% (in quality range [0, 75]). The
compression density loss is around 0.5% (though at some qualities, compression
is little better as well).

Change-Id: I247bc6a8d4351819c871f19d65455dc23aea8650
2012-05-07 14:25:26 -07:00
ec123ca3f6 Forgot to update out_bit_costs to symbol_bit_costs at one instance.
Change-Id: Iaf952c0cb6e3fe35257d2503a16a437c6f2eb3aa
2012-05-07 14:25:25 -07:00
cf33ccd160 Evaluate output cluster's bit_costs once in HistogramRefine.
Avoid bit_costs evaluated every time in function HistogramDistance. Also moved
VP8LInitBackwardRefs and VP8LClearBackwardRefs to backward_references.h

Change-Id: Id507f164d0fc64480aebc4a3ea3e6950ed377a60
2012-05-07 14:25:23 -07:00
781c01f421 Simple Huffman code changes.
No empty trees are codified with the simple Huffman code. The simple Huffman
code is simplified to be either a 1-bit code or 8-bit code for symbols.

Change-Id: I3e2813027b5a643862729339303d80197c497aff
2012-05-07 14:25:21 -07:00
a2849bc502 Lossless decoder: remove an unneeded param in ReadHuffmanCodeLengths().
Change-Id: I279452fdf38b680737d5ba6e868a219281bc8962
2012-05-07 14:25:20 -07:00
b39e7487a7 Reducing emerging palette size from 11 to 9 bits.
This is required to reduce memory used to construct histo_image.

Change-Id: I491a06e10a3e3f3d8a00ecec286394378283ffea
2012-05-07 14:25:19 -07:00
bfc73db4a8 Move GetHistImageSymbols to histogram.c
Planning to revisit memory allocation scheme at several instanaces (next CL).

Change-Id: Id7b9f4854e9577e10b53d5b1d8595b7d862e6e01
2012-05-07 14:25:18 -07:00
889a578681 Improve predict vs no-predict heuristic.
This improves compression density. For example, at quality 95 on 1000 PNGs:
bpp(before) = 2.447 and bpp(after) = 2.412

Change-Id: I19c343ba05cca48a6940293721066502a5c3d693
2012-05-07 14:25:16 -07:00
01f50663dc code-moving and clean-up
* removed use_lz77_ field, and added cache_bits_ one.
* use more BackwardRefs params
* move code around to organize more logically
* reduce memory use on histo
...

Change-Id: I833217a1b950189cf486704049e3fe28382ce335
2012-05-07 14:25:14 -07:00
31035f3b49 reduce memory usage by allocating only one histo
instead of lz77+rle

* introduce VP8LBackwardRefs structure and simplify the code by not passing
  around {PixOrCopy/int} pairs.

More functions should be turned into using this struct (TODO(later)).

Change-Id: I69c5c9fa61dddd61a2abc2824d70b8606a1c55b6
2012-05-07 14:25:12 -07:00
fbb501b8ee Restrict histo_bits to ensure histo_image size is under 32MB
Change-Id: I75ccb65d56ee060b649de714287e71611a92c8e9
2012-05-07 14:25:10 -07:00
8415ddf3be further simplification for the meta-Huffman coding
* don't transmit the number of Huffman tree group explicitly
* move color-cache information before the meta-Huffman block

* also add a check that color_cache_bits is in [1..11] range, as per spec.

Change-Id: I81d7711068653b509cdbc1151d93e229c4254580
2012-05-07 14:25:07 -07:00
e491729905 A quick pass of cleanup in backward reference code
const correctness, renaming, cosmetics etc.

Change-Id: I432befbb22f0eafd9a613f5f632398b6ef03c0f6
2012-05-07 14:25:05 -07:00
83332b3c16 Make transform bits a function of encode method (-m).
Change-Id: Idc392f7cba6e160ea068eacd7f82be4ebc971eaa
2012-05-07 14:25:03 -07:00
72920caa61 introduce -lossless option, protected by USE_LOSSLESS_ENCODER
Change-Id: Ic5707082fefd964b9bcab0c9f9aa276992c22b06
2012-05-07 14:25:02 -07:00
c6ac4dfbb4 Run TraceBackwards for higher qualities.
Also reduce the iteration count in function VP8LHashChain_FindCopy.

Change-Id: I73e3811e142e81314515587fd655ab3bfa74d099
2012-05-07 14:25:00 -07:00
412222c88c Make histo_bits and transform_bits function of quality.
Change-Id: Ic34e40853604811abc63a38e09d6a01961649efc
2012-05-07 14:24:58 -07:00
149b5098a9 Update lossless encoder strategy:
Don't use any other transform when using palette.

Change-Id: I488ac546869677f1b6e4eed80e973569c757e997
2012-05-07 14:24:56 -07:00
0e6fa06595 cache_bits passed to EncodeImageInternal()
should be 0 when not using color cache.

Change-Id: Id15c9b6bfbeb7c69a0189fa4c411df2763f9dead
2012-05-07 14:24:54 -07:00
e38b40a996 Factorize code for clearing HtreeGroup.
Change-Id: I29de6dab7383b8cf071eec155e01340d5fdadee5
2012-05-07 14:24:53 -07:00
6f4a16ea00 Removing the indirection of meta-huffman tables.
Now, the indexing refers directly to 5 huffman codes that must be encoded
separately.

Change-Id: I92e10ccf8ca464aa7259867d5fae2869343e3b3c
2012-05-07 14:24:51 -07:00
3d33ecd12b Some renaming/comments related to palette in lossless encoder.
Change-Id: Iaab32912f4c31e809d7a49fd748099d8c0c3e7d9
2012-05-07 14:24:49 -07:00
4d02d5863f Lossless encoder: correction in Palette storage
(Essentially, there was no need of a separate 'argb_palette' array. And
argb_palette[0] was never being set).

Change-Id: Id0a8c7e063d3af41e39fc9b8661611b51ccc55cd
2012-05-07 14:24:47 -07:00
4a6362357a fix a memleak in EncodeImageInternal()
Change-Id: I55cd013211f192188b54c694ef0837af0c01b53c
2012-05-07 14:24:45 -07:00
0993a611cd Full and final fix for prediction transform
use (tile_size + 1) rows of scratch area.

Change-Id: I06d612fff1794fc045ba76275e94e7210802c332
2012-05-07 14:24:43 -07:00
afd2102f43 Fix cross-color transform in lossless encoder
make elements of "Multiplier" struct unsigned, so that any negative values are
automatically converted to "mod 256" values.

Change-Id: Iab4f9bacc50dcd94a557944727d9338dbb0982f7
2012-05-07 14:24:41 -07:00
b96d8740c9 Need to write a '0' bit at the end of transforms.
Also convert an 'if' to 'assert'.

Change-Id: Ia1129ad9ddb027c27b4f4fc1da4bbaf53a0a8f76
2012-05-07 14:24:39 -07:00
54dad7e553 Color cache size should be counted as 0 when cache bits = 0
Change-Id: I1d05e0561a92aebaf62162fe11ffc4b12496d698
2012-05-07 14:24:37 -07:00
4f0c5caf67 Fix prediction transform in lossless encoder.
(Keep one tile as a scratch buffer).

Change-Id: If112ada29bfd0bdc81b82e849a566b30dd331d2f
2012-05-07 14:24:35 -07:00
36dabdadf8 Fix memory leak in method EncodeImageInternal for histogram_image.
Change-Id: Ia1cfb96d9e6c120630732e2b5f39688376d1d208
2012-05-07 14:24:33 -07:00
352a4f49ab Get rid of PackLiteralBitLengths()
[and in turn a malloc]. Also, a few related const fixes.

Change-Id: I229519b1c34d41c78d9ad2403f1e25feab3c9d93
2012-05-07 14:24:31 -07:00
d673b6b9a0 Change the predictor function to pass left pixel
instead of pointer to the source.

Change-Id: Ia2c8e17c3140709a825c2f85a88c5e31bd6e462f
2012-05-07 14:24:29 -07:00
b2f99465a7 Fix CopyTileWithPrediction()
so that it uses original values of left, top etc for prediction rather than the
predicted values of the same. Also, do some renaming in the same to make it
more readable.

Change-Id: I2fe94e35a6700bd437f5c601e2af12323bf32445
2012-05-07 14:24:27 -07:00
84547f540c Add EncodeImageInternal() method.
Most of changes in enc/vp8l.c is cherry-picked from src/lossless/encode.c

Change-Id: I27938cb2590eccbfe1db0a454343e856bd483e75
2012-05-07 14:24:25 -07:00
6b38378acb Guard the lossless encoder (in flux) under a flag
Change-Id: I6dd8fd17089c199001c06b1afde14233dc3e3234
2012-05-07 14:24:23 -07:00
09f7532cce Fix few nits (const qualifiers)
Change-Id: I527e82af49956b695ab18625d34e143854067421
2012-05-07 14:24:21 -07:00
648be3939f Added implementation for various lossless functions
- VP8LEncAnalyze, EvalAndApplySubtractGreen, ApplyPredictFilter,
  ApplyCrossColorFilter
- Added palette handling and transform buffer management in VP8LEncodeImage()
- Add Transforms (subtract Green, Predict, cross_color) to dsp/lossless.c.

These are more-or-less copied from src/lossless code.

After this Change, will implement the EncodeImageInternal() method.

Change-Id: Idf71f803c24b3b5ae3b5079b15e019721784611d
2012-05-07 14:24:19 -07:00
32714ce3be Add VP8L prefix to backward ref & histogram methods.
Change-Id: I8c14fb219a1d7830d3244aa780c91c9964867330
2012-05-07 14:24:17 -07:00
fcba7be2d3 Fixed header file tag (WEBP_UTILS_HUFFMAN_ENCODE_H_)
Change-Id: I7ccd00361b1b0347639b05ee494e8e701c95cfe3
2012-05-07 14:24:16 -07:00
bc7037465d Add backward_ref, histogram & huffman encode modules from lossless.
Change-Id: Iac056d27972956782defa182caa3ea400cdb77f8
2012-05-07 14:24:14 -07:00
fdccaaddcf Fixing nits
- Const Handling of picture object, removed bitwriter from encoder.

Change-Id: Id943854de09324de81cca615ada960390c4b8152
2012-05-07 14:24:12 -07:00
227110c4c3 libwebp interface changes for lossless encoding.
Change-Id: I703a1a18347acf78378cb23fddc6e5ca6dc6a0bb
2012-05-07 14:24:09 -07:00
50679acf25 minor style fixes
Change-Id: I78a6fa19a799c41d6536631f547279692b658c4d
2012-05-04 19:01:11 -07:00
b38dfccf8d remove unneeded reference to NUM_LITERAL_CODES
Change-Id: I3e98acce3a69fa45054ffcf77644fcbbc04bd366
2012-05-04 19:01:09 -07:00
8979675bdf harmonize header description
Change-Id: Ie0256794e663b58557e7234f08af3d516071001e
2012-05-04 19:01:06 -07:00
c04eb7be9d tcoder.c: define NOT_HAVE_LOG2 for MSVC builds
no version of msvc currently implements log2(). unconditionally define
NOT_HAVE_LOG2 in this case to simplify building libwebp sources in other
projects.

Change-Id: Ia9d985b1125553c5a8271d7e539bc1b4f898d749
2012-05-03 16:59:13 -07:00
9a214fa112 Merge "VP8[L]GetInfo: check input pointers" 2012-05-02 16:07:47 -07:00
5c5be8ba69 VP8[L]GetInfo: check input pointers
validate data before using & width/height before assigning.

Change-Id: I0872e80fcbfea295d7c633b0d4cb7809e1d1883b
2012-05-02 16:05:02 -07:00
0c188feca1 Merge changes I431acdfe,I713659b7
* changes:
  mux: drop 'chunk' from ChunkInfo member names
  muxi.h: remove some unused defines
2012-05-02 16:03:06 -07:00
b3515c6215 mux: drop 'chunk' from ChunkInfo member names
fixes naming style (members should be lowercase, not camelcase).

Change-Id: I431acdfe3cc3740d9effe9df80a94ca284a072b6
2012-05-01 17:47:18 -07:00
aea7923ca2 muxi.h: remove some unused defines
Change-Id: I713659b7ed83bafedd94a0b16379decea293a2f0
2012-05-01 17:47:11 -07:00
0142249285 update NEWS file for next release
Change-Id: I93ba2432fb99334dd17bd1a9da3a39c19e495484
2012-05-01 17:34:14 -07:00
29e3f7ec75 Merge "dec: remove deprecated WebPINew()" 2012-05-01 11:26:31 -07:00
4718e44988 Merge "muxedit: a few more size_t changes" 2012-04-28 03:04:08 -07:00
82654f961d Merge "muxedit: remove a few redundant NULL checks" 2012-04-28 02:50:27 -07:00
02f27fbd3b dec: remove deprecated WebPINew()
Change-Id: I22ad1d297333f5ebc84456d142426a4e0e9a044b
2012-04-27 19:47:35 -07:00
ccddb3fc6f muxedit: remove a few redundant NULL checks
Change-Id: I8285a7694d40b0f5c986dbfe40703251682eaafc
2012-04-27 19:06:03 -07:00
a6cdf7107a muxedit: a few more size_t changes
Change-Id: I18266dc572426cb3daace6e0ad8f2ae21687a151
2012-04-27 18:53:08 -07:00
a384689292 Merge "mux: remove unused LIST_ID" 2012-04-27 03:03:34 -07:00
11ae46ae91 alpha.c: quiet some size_t -> int conversion warnings
Change-Id: I52026a9271bde4028f00df2d752e100c61fd3fe3
2012-04-26 18:47:08 -07:00
dee466926b mux: remove unused LIST_ID
The 'LIST' fourcc is not a well known value in the context of webp.

Change-Id: I6a571f5f3acd6463cf3cad7c5d5ffa89d0597fb6
2012-04-25 18:18:25 -07:00
03f1f49321 mux: add version checked entry points
Change-Id: I3bf5b25b17c06ec092f8ad1c14eea411aa9471c1
2012-04-25 17:27:16 -07:00
6a0abdaa3a Merge "doc: tile/alpha corrections" 2012-04-24 23:57:05 -07:00
c8139fbe5d Merge "few cosmetics" 2012-04-24 23:47:06 -07:00
683387374b Merge "lossless: remove some size_t -> int conversions" 2012-04-24 23:41:26 -07:00
5249e94a22 doc: tile/alpha corrections
- the tile chunk size does not include the alpha or vp8 chunks that
  follow
- remove 'experimental' note from alpha section
- drop an unused term from the terminology section

Change-Id: I46f0fd610bbce579446e19339c3458dddfb595f7
2012-04-24 17:43:06 -07:00
d96e722b0c huffman: quiet int64 -> int conversion warning
children_ is bounded by max_nodes_ and protected with IsFull checks

Change-Id: Iac1eb03c5030568140352174c1265a938fc28d97
2012-04-24 16:17:36 -07:00
532020f24a lossless: remove some size_t -> int conversions
Sizes are given as ints in the documentation and used as such elsewhere.

Change-Id: I51ecd9e501cf9b4e3948aa0e947d2c9b5c85a30f
2012-04-24 16:00:00 -07:00
23be6edfd4 few cosmetics
Change-Id: I85b44d811ecc7c551b75e74e64115e74645836b6
2012-04-24 11:53:10 -07:00
1349edade1 Merge "configure: AC_ARG_* use AS_HELP_STRING" 2012-04-24 10:54:39 -07:00
bfbcc60a18 configure: AC_ARG_* use AS_HELP_STRING
properly formats --help output

Change-Id: I18fad9039400df8415ef192a41c0295019c52775
2012-04-24 10:48:17 -07:00
1427ca8eae Merge "Makefile.am: header file maintenance" 2012-04-24 10:41:59 -07:00
087332e394 Merge "remove unused parameter 'round' from CalcProba()" 2012-04-24 06:41:54 -07:00
9630e16854 remove unused parameter 'round' from CalcProba()
Change-Id: I0d89c34565decb28776aee7500ebd261fc43caf7
2012-04-24 06:40:19 -07:00
92092eaa45 Merge "bit_reader.h: correct include" 2012-04-24 04:19:24 -07:00
a87fc3f609 Merge "mux: ensure # images = # tiles" 2012-04-24 04:18:35 -07:00
53af99b18f Merge "mux: use size_t consistently" 2012-04-24 04:09:44 -07:00
39a57dae22 Makefile.am: header file maintenance
src/dec/Makefile.am: add missing reference to vp8li.h
src/{dec,dsp,enc}/Makefile.am: move some headers to noinst_

Change-Id: I0e2bc69980bd8175d99ad0ab63f537ef9e425b77
2012-04-23 18:53:48 -07:00
1bd0bd0d4d bit_reader.h: correct include
use webp/types.h rather than webp/decode_vp8.h

Change-Id: I9c6da04b92ff00d6dac47ce3eb0bcb2d6a96712d
2012-04-23 17:04:22 -07:00
326a3c6bdc mux: ensure # images = # tiles
when a file contains tiles an image outside of a tile is not allowed.

Change-Id: I5d16759abc9aa9bb299103b2cd7d694fc9c89697
2012-04-23 16:44:37 -07:00
95667b8d86 mux: use size_t consistently
remove mixed use of uint32_t

Change-Id: I57f701a0d9fce255c4dd83c01e523079976ef5b5
2012-04-23 11:00:20 -07:00
231ec1fb6d Removing the indirection of meta-huffman tables.
Now, the indexing refers directly to 5 huffman codes  that must be encoded separately.

Change-Id: I4deeb04de1997e6d20c376046d2053ec7ee918de
2012-04-23 08:36:14 -07:00
15ebcbaaf4 check return pointer from MuxImageGetListFromId
previously, it could crash with nth=1 on a raw vp8 bitstream, e.g.

Change-Id: Ice555d95b984ba71017fc56314d0c2c1b5bdf599
2012-04-23 08:22:58 -07:00
b0d6c4a722 Merge "configure: remove test for zlib.h" 2012-04-21 15:34:41 -07:00
8cccac50ae Merge "dsp/lossless: silence some build warnings" 2012-04-21 15:32:45 -07:00
b08819a624 dsp/lossless: silence some build warnings
src/dsp/lossless.c: In function 'VP8LInverseTransform':
src/dsp/lossless.c:312:23: warning: 'packed_pixels' may be used
uninitialized in this function [-Wuninitialized]
src/dsp/lossless.c:304:16: note: 'packed_pixels' was declared here
src/dsp/lossless.c:258:34: warning: 'm.red_to_blue_' may be used
uninitialized in this function [-Wuninitialized]
src/dsp/lossless.c:275:17: note: 'm.red_to_blue_' was declared here
src/dsp/lossless.c:257:34: warning: 'm.green_to_blue_' may be used
uninitialized in this function [-Wuninitialized]
src/dsp/lossless.c:275:17: note: 'm.green_to_blue_' was declared here
src/dsp/lossless.c:255:33: warning: 'm.green_to_red_' may be used
uninitialized in this function [-Wuninitialized]
src/dsp/lossless.c:275:17: note: 'm.green_to_red_' was declared here

patch by pepijn vaneeckhoudt

Change-Id: Iffa4764487a75479df45e772169325cd9ee60d94
2012-04-20 12:35:35 -07:00
7ae225218d Android.mk: SSE2 & NEON updates
patch by pepijn vaneeckhoudt
- Android.mk should include dec/enc/upsampling sse2 variants. This
  provides sse2 optimizations when compiling for Android/x86
- LOCAL_ARM_NEON should be set to true when compiling for armeabi-v7a.
  Otherwise __ARM_NEON__ is not defined and all neon code is removed by
  the preprocessor.

Change-Id: I54f3505757fc5d2d63cca4b64d61be34a0b34eb8
2012-04-20 12:07:03 -07:00
0a49e3f3da Merge "makefile.unix add missing header files" 2012-04-20 00:34:35 -07:00
2e75a9a1ba Merge "decode.h: use size_t consistently" 2012-04-19 22:49:47 -07:00
fa13035e97 configure: remove test for zlib.h
hasn't been needed since:
  9523f2a Add Alpha Encode support from WebPEncode.

Change-Id: Ia784ac434ec6fd43aade8875a91e2ad9f0fa9c30
2012-04-19 16:54:59 -07:00
d3adc81db2 makefile.unix add missing header files
Change-Id: I440ea805fd14884242bab2577e8286088616e5c7
2012-04-19 16:13:26 -07:00
262fe01be2 Merge "makefile.unix & Android.mk: cosmetics" 2012-04-19 16:03:08 -07:00
4cce137ebf Merge "enc_sse2 add missing stdlib.h include" 2012-04-19 15:51:53 -07:00
80256b8567 enc_sse2 add missing stdlib.h include
lost in fbd82b5; most platforms were getting it indirectly through
emmintrin.h.

Change-Id: I310f8bc8e82d63cfbde74c34cd21b72514a16a01
2012-04-19 15:47:58 -07:00
9b3d1f3a1b decode.h: use size_t consistently
replaces mixed use of int/uint32_t for buffer sizes
further changes the API/ABI.

Change-Id: I91d70fd82ee3e1ac34b884b8ead9a114a9b1015a
2012-04-19 14:31:31 -07:00
64083d3c89 Merge "Makefile.am: cosmetics" 2012-04-19 13:51:33 -07:00
dceb8b4d9a Merge changes If1331d3c,I86fe3847
* changes:
  types.h: centralize use of stddef.h
  vp8io: use size_t for buffer size
2012-04-14 13:01:14 -07:00
0e33d7bf42 Merge "webp/decode.h: fix prototypes" 2012-04-13 10:46:14 -07:00
fac0f12e1b rename BitReader to VP8LBitReader
Change-Id: I192b76422e131a94fb58c2c4a5520a5dba807126
2012-04-13 01:56:31 -07:00
fbd82b5a39 types.h: centralize use of stddef.h
for size_t / NULL

Change-Id: If1331d3cf44296ed0ba9e838eae2f5b1bcaeb61b
2012-04-12 17:14:58 -07:00
2154835f73 Makefile.am: cosmetics
- use common file organization across subdir makefiles
- append lib/source/header list variables and sort

Change-Id: I0653e1c73a4552b0c43d21f321b22b4972d6e87b
2012-04-12 15:53:06 -07:00
1c92bd37f3 vp8io: use size_t for buffer size
changes the decoder ABI

Change-Id: I86fe384739b985bf63bcd9badbbbf4411e1eecae
2012-04-12 15:19:53 -07:00
90ead710dc fix some more uint32_t -> size_t typing
Change-Id: Ibbe85ff4a700b17126a65e9ca5a3fa8cbf00b8a6
2012-04-12 13:06:54 -07:00
cbe705c78a webp/decode.h: fix prototypes
fell out of sync with:
  6860c2e fix some uint32_t -> size_t typing

Change-Id: I897fcd61f6a9767fb6accc5899d8e7763c9f4de8
2012-04-12 12:52:17 -07:00
3f8ec1c21f makefile.unix & Android.mk: cosmetics
split object/source list to single line & sort
remove unnecessary tabs

Change-Id: I1db03555f9578e128c746853084438da2b9186eb
2012-04-12 12:29:57 -07:00
217ec7f4d0 Remove tabs in configure.ac
Change-Id: Ia6cd668523881ca6777a723bfe3460f38d064934
2012-04-12 00:13:06 -07:00
b3d35fc130 Merge "Android.mk & Makefile.vc: add new files" 2012-04-11 15:48:30 -07:00
0df04b9e19 Android.mk & Makefile.vc: add new files
from the lossless additions, fixes the build.

Change-Id: I3243c48f21cfb4e2244614bb5ab12ff94dd684e5
2012-04-11 15:47:04 -07:00
e4f20c5bd5 Merge "automake: replace 'silent-rules' w/AM_SILENT_RULES" 2012-04-11 15:45:55 -07:00
8d254a0927 cosmetics
long line, remove out of date TODO

Change-Id: Ic8a40c9d731178af85645b3e24c1cbd807d7d58b
2012-04-11 15:44:48 -07:00
6860c2ea9d fix some uint32_t -> size_t typing
Change-Id: I078243802a67498dfcd3d15b5f1bebf4b6b3b1bb
2012-04-11 04:27:45 -07:00
4af1858a10 Fix a crash due to max symbol in a tree >= alphabet size
(cherry picked from commit 15253f91567ce7a2a4a3de8d70e54ba4b0641af3)

Change-Id: I4fbeaea4c712dae3bef078aef9d302b44bc39ffb
2012-04-10 23:07:13 -07:00
6f01b830e2 split the VP8 and VP8L decoding properly
* each with their own decoder instances.
* Refactor the incremental buffer-update code a lot.
* remove br_offset_ for VP8LDecoder along the way
* make VP8GetHeaders() be used only for VP8, not VP8L bitstream
* remove VP8LInitDecoder()
* rename VP8LBitReaderResize() to VP8LBitReaderSetBuffer()
(cherry picked from commit 5529a2e6d47212a721ca4ab003215f97bd88ebb4)

Change-Id: I58f0b8abe1ef31c8b0e1a6175d2d86b863793ead
2012-04-10 23:06:58 -07:00
f2623dbe58 enable lossless decoder
import changes from experimental 5529a2e^
and enable build in autoconf and makefile.unix; windows will be treated
separately.

Change-Id: Ie2e177a99db63190b4cd647b3edee3b4e13719e9
2012-04-10 23:06:36 -07:00
b96efd7d50 add dec/vp8i.h changes from experimental
Pulled from 5529a2e^.

Change-Id: I51fe7a9c3acf1be4c43af834fd525ab3be5d7e65
2012-04-10 23:05:57 -07:00
19f6398ed7 add dec/vp8l{i.h,.c} from experimental
Pulled from 5529a2e^.
The history of this and related files is a bit entangled so rather
trying to split the changes and introduce some noise in master's history
we'll start with a fresh snapshot.
The file progression is still available in the experimental branch.

Change-Id: I490a49cb0084d556a29d7c5257c986ea695ce60c
2012-04-10 23:05:14 -07:00
c4ae53c8b3 add utils/bit_reader.[hc] changes from experimental
Pulled from the parent of the current version (5529a2e^).
The history of this and related files is a bit entangled so rather
trying to split the changes and introduce some noise in master's history
we'll start with a fresh snapshot.
The file progression is still available in the experimental branch.

Change-Id: I6dae97fc381cd6c1d1640c4c565b2084a41ec955
2012-04-10 17:48:49 -07:00
514d008921 add dsp/lossless.[hc] from experimental
Pulled from the current HEAD (218c32e).
The history of this and related files is a bit entangled so rather
trying to split the changes and introduce some noise in master's history
we'll start with a fresh snapshot.
The file progression is still available in the experimental branch.

Change-Id: I40538799dbf999abb9408ac83f55b897d8e22498
2012-04-10 17:37:44 -07:00
9c67291d67 add utils/huffman.[hc] from experimental
Pulled from the current HEAD (218c32e).
The history of this and related files is a bit entangled so rather
trying to split the changes and introduce some noise in master's history
we'll start with a fresh snapshot.
The file progression is still available in the experimental branch.

Change-Id: Ie57be21bf50ad83808c72aeb5fc706d9954d01d8
2012-04-10 17:31:54 -07:00
337914a036 add utils/color_cache.[hc] from experimental
Pulled from the current HEAD (218c32e).
The history of this and related files is a bit entangled so rather
trying to split the changes and introduce some noise in master's history
we'll start with a fresh snapshot.
The file progression is still available in the experimental branch.

Change-Id: Id879be453a94d9f44ec8d47747823ca7297ae008
2012-04-10 17:23:38 -07:00
b3bf8fe7a1 the read-overflow code-path wasn't reporting as an error
(since 'ok' was '1' by default)
(cherry picked from commit fe5eb4ffae74ff493e92fb5cd9a7a1625a113a7b)
2012-03-30 18:58:30 -07:00
1db888ba40 take colorspace into account when cropping
(cherry picked from commit 4e33653b52637c74ae30adde7990265d1d43994b)

Conflicts:

	src/dec/vp8l.c
2012-03-30 12:48:24 -07:00
61c2d51fd7 move the rescaling code into its own file and make enc/ and dec/ use it.
(cherry picked from commit 8e92d9e380a89b7443a2e2c3d16ce5a222e8c1e8)

Conflicts:

	Android.mk
	makefile.unix
	src/dec/vp8l.c
	src/utils/Makefile.am
2012-03-30 12:16:25 -07:00
efc2016a31 Make rescaler methods generic
to be used in rescaling of lossless bitstreams.
(cherry picked from commit 2e12a3045498b6faf13b93bc25391e8226119f0a)
2012-03-30 11:57:07 -07:00
3eacee8158 Move rescaler methods out of io.c.
(cherry picked from commit b5e9db1824f6efbe0bfb87e94b8d6e88a7973ef7)
2012-03-30 11:56:51 -07:00
a69b893dda automake: replace 'silent-rules' w/AM_SILENT_RULES
silent-rules is a new option with automake-1.11; older versions will
fatally exit with it. Using AM_SILENT_RULES will only result in a
warning at least through automake-1.9.

Change-Id: Ieae6a09780efd85f08f0c6442172634a1ce5ff33
2012-03-28 22:46:12 -07:00
6f7bf645b4 issue 111: fix little-endian problem in bit-reader
patch by naideflan

Change-Id: I874dbd5588d5cd2559c54ca9ad5582fa3a589b1b
2012-03-27 05:55:20 -07:00
ed278e2279 Removed unnecessary lookup
The lookup value is the same as the index.  The index in this
case is either a 0 or 1.

Change-Id: I0432dfcbea9e7bb20bca7574e25fb1fd8a51c8e9
2012-03-26 11:44:04 -04:00
cd8c3ba78f fix some warnings: down-cast and possibly-uninitialized variable
Change-Id: I5baf59bbcd5dcff69316b0601483ff5abe8e1454
2012-03-16 20:20:33 -07:00
0a7102ba60 ~1% improvement of alpha compression
by packing the symbol map more efficiently.

This is mainly useful in making it harder to generate invalid bitstream:
before this change, one could code the same symbol twice. Now, it's
impossible, since we code the position using empty symbol slots, instead
of total position.

* Fix the HasOnlyLeftChild() naming while at it.

Change-Id: I63c56c80a4f04a86ac83eded1e3306329815b6c9
2012-02-24 19:59:40 -08:00
3bc1b14191 Merge "Reformat container doc" 2012-02-24 19:30:40 -08:00
dc17abdcb6 mux: cosmetics
add a few missing consts and remove an unnecessary cast

Change-Id: I396571090ce379bbc7a173cbc90714a6b7b52a2a
2012-02-23 13:21:43 -08:00
cb5810dfbe Merge "WebPMuxGetImage: allow image param to be NULL" 2012-02-22 21:58:48 -08:00
506a4af2fa mux: cosmetics
group like parameters together in prototypes, comments, move variable
declarations closer to first use.

Change-Id: Idd6bd87d0366d783fed83f4dd21bd7968cbe6948
2012-02-22 16:14:32 -08:00
135e8b1942 WebPMuxGetImage: allow image param to be NULL
but not both alpha & image

Change-Id: I6822a740de7577eedcbe7529382aa2c020d1e8e4
2012-02-22 16:09:06 -08:00
de556b6866 Merge "README.mux: reword some descriptions" 2012-02-22 15:02:50 -08:00
0ee2aeb904 Makefile.vc: use batch mode rules
speeds up the build

Change-Id: I2471fb6b42bb5d51f8dd1634f65d5f8114355a22
2012-02-22 11:36:28 -08:00
d9acddc010 msvc: move {i,p}db creation to object directory
avoids cluttering the source tree.

Change-Id: I2885b36dd7152e294536584fa5cfe3ae8b04c7fa
2012-02-22 11:19:16 -08:00
237c9aa727 Merge "expose WebPFree function for DLL builds" 2012-02-22 10:55:52 -08:00
b3e4054f14 silence msvc debug build warning
_byteswap_ulong is defined in stdlib.h, release builds seem to pull it
in through a different path.

Change-Id: I510d2624150f89a4a77734bf3dc5b4db60a4ba95
2012-02-21 13:57:48 -08:00
45feb55dec expose WebPFree function for DLL builds
This allows managed code (e.g., C#) to free buffers returned by the API
instead of relying on free().
Based on suggestion from Steven (grokys at googlemail dot com).
Fixes issue #109.

Change-Id: I94826a77f6f4cb6e331c454f994c964e2e448cae
2012-02-21 13:48:06 -08:00
11316d84b4 README.mux: reword some descriptions
Change-Id: I334caaa908c9a50baf8e35125dd0062d764f2949
2012-02-17 14:39:31 -08:00
4be52f4a65 factorize WebPMuxValidate
move count extraction, count and feature flag validation to
ValidateChunk.

Change-Id: I4bd81bbc8a5a48e1263d11992ab3f285c45417b8
2012-02-15 11:54:10 -08:00
14f6b9f606 mux: light cleanup
- const / move declarations closer to first use
- remove unnecessary ()s
- don't return int unnecessarily with internal Init/Release
- compact some lines

Change-Id: If7ab505e417221debc356f21f075506939110a84
2012-02-14 13:51:41 -08:00
5e96a5dbef add more param checks to WebPPictureDistortion()
and use the proper alpha flag too.

Change-Id: I664c6c9dbcc747495bf20b3da95d00e1120b5baf
2012-02-10 02:52:48 -08:00
8abaf82083 Merge "silence some type size related warnings" 2012-02-09 19:21:39 -08:00
1601a39b09 silence some type size related warnings
size_t/ptrdiff_t -> uint32_t

Change-Id: Ic4d889b9239752311b9ed906c83076829d850874
2012-02-09 13:00:09 -08:00
f3abe520ac Merge "idec: simplify buffer size calculation" 2012-02-09 01:25:00 -08:00
a9c5cd4c97 idec: simplify buffer size calculation
Change-Id: Id0e20c44704ef9b0e14dcf5e71a05cea488fd95a
2012-02-08 14:29:25 -08:00
7b06bd7fcb Merge "configure/automake: add silent-rules option" 2012-02-08 14:04:12 -08:00
e9a7d145e7 Reformat container doc
- split the doc into sections for simple and extended format and move
  example layouts to the end.
- use ASCII tables to describe chunk formats
- attempt to consistently use MUST/SHOULD, etc.
- remove bold from most terms, but add them to definition lists which
  allow for the styling to be changed.

Change-Id: I93c1cd33bde9ccf0b265b202ec4182ce98fd6b48
2012-02-08 11:32:42 -08:00
d4e5c7f327 configure/automake: add silent-rules option
Enables --(enable|disable)-silent-rules and make V=[01] to control
compile output. Default is verbose as before.

Change-Id: Id9a65977ac8c719f4d9e3efe386750e520689b76
2012-02-07 18:08:08 -08:00
5081db78be configure/automake: no -version-info for convenience libs
Silences:
libtool: link: warning: `-version-info/-version-number' is ignored for
convenience libraries

Change-Id: I5705383b58f529fb06c2bf0932976b5a202446b6
2012-02-07 18:06:20 -08:00
85b6ff6897 Merge "idec: fix WebPIUpdate failure" 2012-01-31 23:39:02 -08:00
7bb6a9ccd6 idec: fix internal state corruption
A call to Append/Update would index the parts_ array w/-1 as num_parts_
had yet to be set by DecodePartition0. This would cause corruption
within the VP8Decoder member.

Fixes issue #106.

Change-Id: Ib9f2811594ff19e948a66fda862a4e0a384bb9aa
2012-01-31 17:10:29 -08:00
89cd1bb85b idec: fix WebPIUpdate failure
If the first call to WebPIUpdate contained all of partition 0, but not
enough to decode a macroblock the test for max macro block size could
fail (4096 bytes) due to a memory buffer offset not being updated.
This change offsets the buffer in the same manner as Append().

Fixes issue #105.

Change-Id: I90ca3d0dde92eedc076933b8349d2d297f2469e8
2012-01-31 16:55:10 -08:00
01b6380656 4-5% faster decoding, optimized byte loads in arithmetic decoder.
Bits are loaded 32bits at a time (and often aligned).
Rather 64bit-friendly

Change-Id: If7f67dbe5e37696efbeb6d579d9d8482350b79ee
2012-01-31 02:07:15 -08:00
631117ea5e Merge "cosmetics & warnings" 2012-01-30 22:04:11 -08:00
a0b2736d79 cosmetics & warnings
- remove some unused functions
- move global arrays from data to read only section
- explicitly cast malloc returns; not specifically necessary, but helps
  show intent
- miscellaneous formatting

Change-Id: Ib15fe5b37fe6c29c369ad928bdc3a7290cd13c84
2012-01-30 17:19:53 -08:00
f73947f41d use 32bit for storing dequant coeffs, instead of 16b.
is a tad faster

Change-Id: Ibef8f3b4f3f04a9a647098e5946f616e211a61cd
2012-01-29 17:38:37 -08:00
b9600308e8 Merge "store prediction mode array as uint8_t[16], not int[16]." 2012-01-27 07:16:50 -08:00
7b67881a23 store prediction mode array as uint8_t[16], not int[16].
This allow faster copy and gives a little bit of speed-up

Change-Id: I5f478229766098630b53b8a4982442fe29290ee6
2012-01-27 03:17:41 -08:00
cab8d4dc22 Merge "NEON TransformOne" 2012-01-26 11:38:16 -08:00
ba503fdac6 NEON TransformOne
As with the loop filter, implementing this with intrinsics is difficult
because we require subscript access for reading and writing 32 bits at a
time.

Approximately 5% decode speed improvement. This could be increased by
exposing TransformOne and rewriting TransformTwo to only handle dual
IDCTs.

Change-Id: Idd409264ab5d154a537107a1d54b419a48f7c1a8
2012-01-26 11:37:32 -08:00
9f740e3b1a Merge "gcc warning fix: remove the 'const' qualifier." 2012-01-25 15:53:10 -08:00
f76d3587e5 gcc warning fix: remove the 'const' qualifier.
Change-Id: I47b8f147f54072cb9b62669efd78c7b958aa5c08
2012-01-25 15:50:24 -08:00
e78478d62c Merge "webpmux: make more use of WebPData" 2012-01-25 13:06:49 -08:00
f85bba3dae Merge "manpages: add BUGS section" 2012-01-25 12:43:16 -08:00
48a43bbfae Merge "makefile.unix: variable cosmetics" 2012-01-25 12:42:09 -08:00
c274dc9679 makefile.unix: variable cosmetics
use CPPFLAGS to hold include paths, makes overriding CC a bit more
intuitive.

Change-Id: I6ecfc1957cae423dd150ebf37b878f1da6bbcef3
2012-01-25 10:46:28 -08:00
1f7b8595ae re-organize the error-handling in the main loop a bit
Change-Id: Id74298131df9c33a86b989e15c11ffb6d04960d8
2012-01-25 08:34:58 -08:00
1336fa719d Only recompute level_cost_[] when needed
Add a dirty_ flag to keep track of updated probabilities and the need to
recompute the level costs.
This only makes a difference for "-m 2" method which was sub-optimal.
But it's overall cleaner to have this flag.

Change-Id: I21c71201e1d07a923d97a3adf2fbbd7d67d35433
2012-01-25 07:52:34 -08:00
771ee449b0 manpages: add BUGS section
reorder sections to better match 'man man-pages'

Change-Id: I6cc3cd7f74b6cc6848c407cb1f042207c82f847e
2012-01-24 17:40:42 -08:00
0f7820e65c webpmux: make more use of WebPData
Add WebPDataCopy and use it in ReadImage which now returns WebPData*s.
Update ReadData prototype variables used with it to avoid unnecessary
const casting back and forth from void*.

Change-Id: I3f83db6729306eab0db9027b68706c8a7005ec10
2012-01-24 17:18:45 -08:00
974aaff360 examples: logging updates
- send errors to stderr
- send help to stdout
- add image size to webpmux -info output and send to stdout
- correct webpmux exit values

Change-Id: Ifd8e8493aab33a82765f7b7903cef345d96da9ae
2012-01-24 16:55:38 -08:00
6c14aaddc4 Merge "better token buffer code" 2012-01-23 18:55:11 -08:00
f40542508c better token buffer code
(still not finished, but compiles and works ok)

Change-Id: I7002bf8017e31e7af34a53126072b625d23e2589
2012-01-23 18:54:24 -08:00
18d959fa9a Merge "mux: add WebPData type" 2012-01-23 18:00:21 -08:00
eec4b87718 mux: add WebPData type
slightly simplifies Get process

Change-Id: I9db365da29038e02a032494e0ef3d8a0d7515210
2012-01-23 17:51:01 -08:00
0de3096b3a use 16bit counters for recording proba counts
This proved being ok, even for large pictures, provided one
takes care of overflow. When an overflow is bound to occur, the
counters are renormalized.
Overall, shaves ~12k of memory.

Change-Id: I2ba21a407964fe1a34c352371cba15166e0c4548
2012-01-23 17:50:58 -08:00
7f23678da0 fix for LevelCost + little speed-up
the p0 proba was incorrectly accumulated. Merging its contribution into
the LevelCost[] was creating more problems than anything (esp. with trellis)
so let's just not.

Change-Id: I4c07bfee471085df901228d97b20a4d9606ba44e
2012-01-23 09:21:17 -08:00
7107d54483 further speed-up/cleanup of RecordCoeffs() and GetResidualCost()
(note: Incorporated the cost of bin #1 into the LevelCost[])

Change-Id: I6408b2a822efdb97ad6a3a21d380bc7b0da9c715
2012-01-23 07:01:37 -08:00
fd22104022 Introduce Token buffer (unused for now)
Later, will be useful to collect stats and code bitstream in one single pass

Change-Id: Icf866473958b5eff879e219dbb1d7e350f2268ba
2012-01-23 05:57:12 -08:00
5fa148f47c Merge "speed-up GetResidualCost()" 2012-01-23 02:50:58 -08:00
28a9d9b41a speed-up GetResidualCost()
+ misc cosmetics and code polishing

Change-Id: I5830cd2f268d64c072b1cbccc0a4674833875055
2012-01-23 02:36:38 -08:00
11e7dadd96 Merge "misc cosmetics" 2012-01-22 23:52:29 -08:00
378086bd0a misc cosmetics
Change-Id: Iafc73f9fece13b5ca900a8b9c1039268872a181b
2012-01-22 23:20:53 -08:00
d61479f95b add -print_psnr and -print_ssim options to cwebp.
These will report the 7x7-averaged PSNR or SSIM, using the
new internal function WebPPictureDistortion().
This is for information only. These flags have no encoding impact.

+misc opportunistic cosmetics

Change-Id: I64c0a7eca679134d39062e438886274b22bb643f
2012-01-20 07:37:29 -08:00
2e3e8b2ef5 add a WebPCleanupTransparentArea() method
to 'clean up' the fully-transparent area and make it more compressible
new cwebp flags: -alpha_cleanup (off by default, since gain is not 100% guaranteed)

Change-Id: I74d77e1915eee146584cd61c9c1132a41db922eb
2012-01-18 14:01:24 -08:00
552c121715 Merge "mux: plug some memory leaks on error" 2012-01-18 13:46:38 -08:00
a2a81f7d61 Merge "fix Mach-O shared library build" 2012-01-18 13:16:12 -08:00
b3482c4300 Merge "fix gcc-4.0 apple 32-bit build" 2012-01-18 13:15:23 -08:00
e4e3ec19ad fix gcc-4.0 apple 32-bit build
gcc-4.0 defines __PIC__ but not __pic__. This leaves the test for
__pic__ should the inverse case exist.
Fixes issue #103; build failing with:
"error: can't find a register in class 'BREG' while reloading 'asm'"

Change-Id: Ia767a733de6ce0294146f9477ff9c46f0ebe13b0
2012-01-18 13:12:45 -08:00
b0d2fecf25 mux: plug some memory leaks on error
Make sure chunk->data and wpi are not leaked by
MuxAddFrameTileInternal() in case of MEMORY_ERROR in ChunkSetNth().

Change-Id: Ie20e84b92f4bdcb7c3b94520f36b20dd2e730545
2012-01-17 18:40:25 -08:00
f0d2c7a76f pass of cosmetics
Change-Id: Id031221b7499e8cfc460c873d0f8124c9bef3aa3
2012-01-17 18:32:27 -08:00
b309a6f901 fix Mach-O shared library build
Add extern to kChunks[] in muxi.h.
Fixes:
ld: duplicate symbol _kChunks in .libs/muxinternal.o and .libs/muxedit.o

Change-Id: Ibeb060f7fdec5fe904097a2443f0cda2f7ede884
2012-01-17 16:51:19 -08:00
241ddd38e2 doc: delete mux container pdf
The source is now webp-container-spec.txt.

Change-Id: I43debf616b7eec774b5e1586c286974c7332fb48
2012-01-17 12:54:51 -08:00
8b1ba272d2 doc: update VP8 decode guide link
The draft is now RFC6386.

Change-Id: Ief019357af5a5de9c6290cafd34db09d984f58aa
2012-01-17 12:52:27 -08:00
7e4371c5b3 WebPMuxCreate: fix unchecked malloc
Change-Id: Ic824cdfbede4abe8e5106904dd17d564bb1b32d4
2012-01-15 11:18:23 -08:00
eb425586e1 Merge "have makefile.unix clean up src/webp/*~ too" 2012-01-13 14:27:46 -08:00
a85c363186 Merge "correct EncodeAlpha documentation" 2012-01-13 14:27:03 -08:00
a33842fdb0 Merge "Update webp container spec with alpha filter options." 2012-01-13 14:25:39 -08:00
8d6490dae6 Incremental support for some of the mux APIs.
This CL adds incremental support for WebPMuxCreate() and
WebPMuxGetFeatures()

Change-Id: I312122f5e817d58366e18ae238cb9e2a493811fb
2012-01-13 14:47:53 +05:30
b8375abd08 have makefile.unix clean up src/webp/*~ too
Change-Id: Ida17b92346055b197fe6ccb60a9ac223ddd46f90
2012-01-12 18:49:29 -08:00
b5855fc78d correct EncodeAlpha documentation
The paeth filter was removed in 8ca2076

Change-Id: I743502147da4e0b5506e49382311b8f592973d41
2012-01-12 18:37:45 -08:00
dba37fea0e Update webp container spec with alpha filter options.
Change-Id: I032f350c8c698aeb139f7dd148f01a0a3b012b6d
2012-01-12 18:25:00 -08:00
2e74ec8b5f fix compile under MINGW
there was a type conflict around INT32 (in libjpeg).

Patch by nipmarsh
Issue 102: http://code.google.com/p/webp/issues/detail?id=102

Change-Id: I6425b6b9b9861bcaa8bb1adc8a78541e44031a6e
2012-01-12 04:53:30 -08:00
716d1d7f87 fix suboptimal MAX_LEN cut-off limit
MAX_LEN -> max_len
This was sub-optimal at the end of the picture, when there's
less than MAX_LEN bytes left to match.

Change-Id: I5ebe1fca4e7c112dcd34748a082d1c97f95eb099
2012-01-10 07:41:11 -08:00
57cab7b891 Harmonize the alpha-filter predictions at boundary
often reduces compressed size by ~10's of bytes
+ refactored / sped-up the prediction code (gradient: ~30% faster)

Change-Id: I26bd983655dad4f85d5c5ddc20a1980f384c4dd6
2012-01-10 03:21:13 -08:00
3a9895340f Merge "Fix bug for Alpha in RGBA_4444 color-mode." 2012-01-09 02:59:59 -08:00
8ca2076de1 Introduce a 'fast' alpha mode
.. where only 2 filtering modes are potentially
tried, instead of all of them. This is fast than the exhaustive 'best'
mode, and not much worse.

Options for cwebp are:
 -alpha_filter none
 -alpha_filter fast      (<- default)
 -alpha_filter best      (<- slow)

Change-Id: I8cb90ee11b8f981811e013ea4ad5bf72ba3ea7d4
2012-01-09 02:37:44 -08:00
221a06bb16 Fix bug for Alpha in RGBA_4444 color-mode.
Fix bug for Alpha data in RGBA_4444 color-mode.
The Alpha data is required to be clipped [0, 15] while
converting from 8 bits to 4 bits.

Change-Id: I80705d575c35121beb9633a05ec8823435c79586
2012-01-09 14:07:30 +05:30
ad1e163a0d cosmetics: normalize copyright headers
Change-Id: I5e2462b101e0447a4f15a1455c07131bc97a52dd
2012-01-06 14:49:06 -08:00
c77424d7eb cosmetics: light include cleanup
remove some unused includes

Change-Id: I07fabf707e8eac7f204757e68dab1057fd21aef1
2012-01-06 14:38:34 -08:00
9d0e17c9a9 fix msvc build breakage after 252028a
replace inline with WEBP_INLINE; inline is reserved for c++ code
w/visual studio

Change-Id: Ib06c6a12fd786701740d8b260d9aa310ee81fddf
2012-01-05 18:26:40 -08:00
7c4c177c39 Some readability fixes for mux library
Add a #define for UNDEFINED_CHUNK_SIZE and remove a misleading comment.

Change-Id: I5c56a12f1245e647f913c35c6a34d2e5d658415e
2012-01-05 17:53:51 +05:30
d8a47e66f7 Merge "Add predictive filtering option for Alpha." 2012-01-05 00:33:32 -08:00
252028aaac Add predictive filtering option for Alpha.
Add predictive filtering option for Alpha plane.
Valid range for filter option is [0, 5] corresponding to prediction
methods none, horizontal, vertical, gradient & paeth filter.
The prediction method 5 will try all the prediction methods (0 to 4)
and pick the prediction method that gives best compression.

Change-Id: I9244d4a9c5017501a9696c7cec5045f04c16d49b
2012-01-05 13:55:35 +05:30
9b69be1cdc Merge "Simplify mux library code" 2012-01-05 00:15:48 -08:00
a056170eca Simplify mux library code
Refactor mux code into Read APIs, Set/Delete APIs and internal
objects/utils.

Change-Id: Ia4ce32ec18cf0c1c75de9084fbb28840d46892b4
2012-01-05 12:23:08 +05:30
992187a380 improve log2 test
- add check for native log2 to configure
- use a common define (NOT_HAVE_LOG2) to enable use of local library
  version for non-autoconf platforms without their own version,
  currently msvc and android

This uses a negative (NOT_HAVE_) to simplify the ifdef

Change-Id: Id0610eed507f8bb9c5da338918112853d5c8127a
2012-01-04 18:49:37 -08:00
e852f83205 update Android.mk file list
Change-Id: Idffc497931c85582725e65f1d7b7384bcee0fdd5
2011-12-26 02:39:56 -08:00
a90cb2be6e reduce number of copies and mallocs in alpha plane enc/dec
Change-Id: Ice48d2dd403c18870567ee6e1b210b1eb3d5b44f
2011-12-23 01:21:53 -08:00
b1662b0530 fix some more type conversion warnings w/MSVC
API & memory related warnings will be dealt with separately

Change-Id: I8a7893baa4d5035c963fb3c16e0bb4eee742e968
2011-12-21 16:07:16 -08:00
223d8c60aa fix some uint64_t -> int conversion warnings with MSC
Change-Id: I5dc314c9d1a3cbda095be3c90886983500104867
2011-12-21 14:43:52 -08:00
c1a0437b87 Merge "simplify checks for enabling SSE2 code" 2011-12-20 17:42:59 -08:00
f06817aaea simplify checks for enabling SSE2 code
also fixes build issues under vs11 which has a native arm compiler for
windows 8 targets

Change-Id: Id76c2deae9fc9de147d13ad0d34edffcb5a726c4
2011-12-20 17:41:55 -08:00
948d4fe94f silence a msvc build warning
implicit double -> float conversion; avoiding sqrtf (c99) for
compatibility

Change-Id: If9b367dde237fc5173c1445cc60b16aa6ad2821c
2011-12-19 23:04:01 -08:00
911795490e vwebp: msvc build tweaks
snprintf -> _snprintf / remove double->float warnings

Change-Id: I5dcdc394cf7e2d37205beaf83ed0f80a18ec06ab
2011-12-16 19:30:33 -08:00
7937b409e7 simple WebP viewer, based on OpenGL
Quite handy and simple (for now).
It's not yet incorporated within the build system (autotools or
makefile.unix), because it gets complicated with OpenGL. TODO(later).

For now, there's some ready-to-use command line for compiling on Linux
or Mac in the header of vwebp.c

Later, this tool will be supplemented with support for animation,
tiling, zooming, etc.

Change-Id: I292972cea4862536afbe8c9ec444c590d152f086
2011-12-13 14:02:04 -08:00
6aac1df17b add a bunch of missing 'extern "C"'
Change-Id: I8c0ada049ce9fa7ef14164b90d58e999cdabba53
2011-12-13 10:51:32 -08:00
421eb99db3 Merge "Remove assigned-but-not-used variable "br"" 2011-12-08 10:41:18 -08:00
91e27f4573 better fitting names for upsampling functions
Change-Id: I816e81586c9e1a74ebc5516598dbd4ae0ddf48d8
2011-12-08 06:42:53 -08:00
a5d7ed5c4b Remove assigned-but-not-used variable "br"
Fixes gcc 4.6.x -Wunused-but-set-variable compiler warning:

	libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I../.. -Wextra -Wall -Wunused-but-set-variable -MT libwebpdecode_la-idec.lo -MD -MP -MF .deps/libwebpdecode_la-idec.Tpo -c idec.c  -fPIC -DPIC -o .libs/libwebpdecode_la-idec.o
	idec.c: In function 'DecodeRemaining':
	idec.c:371:17: warning: variable 'br' set but not used [-Wunused-but-set-variable]

Change-Id: Ib037be9634b5b802ba9f1e2ef156a8fdf9630752
2011-12-08 15:42:01 +01:00
f62d2c9497 remove unused 'has_alpha' from VP8GetInfo() signature
alpha information is not to be found at RIFF chunks level, not
in the VP8 bitstream (that was a tmp hack)

Change-Id: Idd1629c696b03c26f6f30650d7216f627f1761df
2011-12-08 06:41:40 -08:00
08e865822a trap alpha-decoding error
Improves robustness to bad bitstream.

Change-Id: I87ca03ccd12d4d690904b35f8dfc2a20a28fb47b
2011-12-08 06:34:00 -08:00
b361eca1c5 add cut-off to arith coder probability update.
This speeds things up for long message, while not damaging
the stats too much for usual-sized cases

Change-Id: I3f45e28b771d701e2e1da11eb800de18c4ed12fc
2011-12-08 06:27:11 -08:00
8666a93aae Some bug-fixes for images with alpha.
- Fix the off-by-one diff when cropping with simple-filter.
- Fix a bug in incremental decoding in case of alpha.
- In VP8FinishRow(), do not decode alpha when y_start > y_end.
- Correct output of alpha channel for MODE_ARGB.
- Correct output of alpha channel for MODE_RGBA_4444.

Change-Id: I785763a2a704b973cc742ad93ffbb53699d1fc0a
2011-12-07 15:12:50 +05:30
273a12a013 fix off-by-1 diff in case cropping and simple filtering
Change-Id: I6dea172d0cf129f9986b8a3e1eea85fa97a70402
2011-12-05 08:37:55 -08:00
2f741d1e13 webpmux: ReadImage: fix ptr free in error case
allocate to temporaries and deal with output pointers in one place to
reduce casting.

Change-Id: Ib12d866440184315536f35a15eb5adeba7879ae7
2011-12-02 17:58:20 -08:00
721f3f48e7 fix alpha decode
- remove left-over WEBP_EXPERIMENTAL_FEATURES #ifdef

Change-Id: I2b91ccd2e2b3ebea63d5f698f90206feec30a4db
2011-12-02 11:00:27 -08:00
60942c8cea fix the has_alpha_ order
Change-Id: I26211732584f6181bf763497818b45515e394c21
2011-12-02 10:26:42 -08:00
30971c9e95 Implement progress report (and user abort)
New cwebp flag is -progress

Change-Id: Ied872cca13f512036860783bbee1bdbccad72768
2011-12-01 14:19:40 -08:00
eda520a92e cosmetics after 9523f2a
spelling, indent and other style conformance

Change-Id: Iad720c0d85f2147676e302439d57628712d3b109
2011-12-01 13:05:31 -08:00
38bd5bb524 Merge "Better alpha support in webpmux binary" 2011-12-01 06:01:33 -08:00
ccbaebfe30 Merge "Updated the includes to relative paths." 2011-12-01 04:20:46 -08:00
d71fbdcca3 fix small typo in error message array
Change-Id: If34537e53cc56b5f5daa15afaefb1f49d11602f5
2011-12-01 03:34:22 -08:00
cdf97aa265 Better alpha support in webpmux binary
- Support for adding/getting frames/tiles with alpha in webpmux
- For "-info" option in webpmux binary, add alpha related info

Change-Id: Iccd2fd35ab8453dabd74779f12f2844ab76d0538
2011-12-01 16:15:57 +05:30
885f25bc83 Updated the includes to relative paths.
Change-Id: I0ea1a59dd0f6cb1df9f41e048e0c5a13e7fb8335
2011-12-01 15:30:41 +05:30
a0ec9aace9 Update WebP encoder (cwebp) to support Alpha.
Updated cwebp (Webp Encoder) binary to support Alpha encoding.
Modified man page and WebP container spec appropriately.

Change-Id: I52f6a5cb3e870c386591e9a7776293fa6a8fb04b
2011-12-01 15:13:17 +05:30
667b769aee Fixed the include for types.h within mux.h
Made the include (for types.h) to relative path w.r.t mux.h

Change-Id: Ifb2a36557d7c8b63490738a2cc48e03993d24639
2011-12-01 13:47:32 +05:30
9523f2a5de Add Alpha Encode support from WebPEncode.
Extend WebP Encode functionality to encode Alpha data and produce
bit-stream (RIFF+VP8X+ALPH+VP8) corresponding to WebP-Alpha.

Change-Id: I983b4cd97be94a86a8e6d03b3b9c728db851bf48
2011-12-01 12:15:58 +05:30
16612ddd30 Merge "Add Alpha Decode support from WebPDecode." 2011-11-30 22:02:03 -08:00
d117a94046 Add Alpha Decode support from WebPDecode.
Extend WebP decode functionality to extract Alpha data (support ALPH
chunk) and decode the Alpha plane using Alpha (utils/alpha) core-lib.

Change-Id: I6f0ee1b189c13caba3c1dd9b681383bfd00aa212
2011-12-01 11:24:50 +05:30
67228734dc cosmetics after e1947a9
indent / spelling / line length & a couple missing consts

Change-Id: Id282cb7bc6881d4738a3d9fa912b5b22a007c4d2
2011-11-30 18:51:20 -08:00
e1947a9299 Add Alpha encode/decode code.
Add code for Alpha encoding & decoding. The alpha compression is done
via backward reference counts encoded with Arithmetic encoder (TCoder).
Also provided is lossy Alpha pre-processing option via level-quantizations
using kNN heuristic.

Change-Id: Ib6b13530c1a4ab6493edcb586ad29fe242bc1766
2011-11-30 17:53:09 +05:30
afc4c5d695 simplify code by introducing a CopyPlane() helper func
Change-Id: Id1a2a5d8a3bf48cf614213442c1386a77c0d6880
2011-11-23 18:00:39 -08:00
113b312881 Merge "MUX API Updates" 2011-11-23 05:58:02 -08:00
c398f59536 MUX API Updates
- Add alpha support in mux.
- Remove WebPMuxAddNamedData() and WebPMuxGetNamedData() APIs. Add WebPMuxSetImage(), WebPmuxGetImage() and WebPMuxDeleteImage() APIs instead.
- Refactor code using WebPImage struct.
- Corresponding changes in webpmux binary.
- WebPMuxSetImage()/AddFrame()/AddTile() can now work with data which starts from "RIFF...". This simplifies reading a single-image webp file and adding it as an image/frame/tile in mux.

Change-Id: I7d98a6407dfe55c84a682ef7e46bc622f5a6f8d9
2011-11-23 19:03:13 +05:30
5acf04ef8c remove orphan source file
similar to 8962030 enc/enc_sse2.c became a part of dsp/ w/e06ac08.

Change-Id: Ia0cef3133e3f8ec5da0bd7a5172c22e86f1d41cb
2011-11-19 11:01:19 -08:00
059f03ef99 Merge "dec: validate colorspace before using as array index" 2011-11-17 10:24:59 -08:00
70a0398988 Merge "factorize some code" 2011-11-16 19:14:49 -08:00
9b243b3db0 factorize some code
Change-Id: Iac4cf0cf05888b7dd6613028dac619d8f5b0501b
2011-11-16 19:00:27 -08:00
372e2b463b Correct a bug in ReadPNG() with GRAY_ALPHA images
Change-Id: I4676160ee544ed914cc0de3f9cd140d9ee65addc
2011-11-16 18:56:23 -08:00
469d6eb974 Merge "Makefile.am: remove redundant noinst_HEADERS" 2011-11-16 18:41:55 -08:00
9fe3372f19 dec: validate colorspace before using as array index
Change-Id: I5bcf01769d58d39a5f8d50f54cf0cd83a2309602
2011-11-16 18:21:56 -08:00
8962030f52 remove orphan source file
enc/enc.c became a part of dsp/ w/e06ac08.

Change-Id: I9e99aa6eac7b28162f58f3f0dc180a68a279d136
2011-11-14 19:31:02 -08:00
ced3e3f4e0 Makefile.am: remove redundant noinst_HEADERS
When they appear in _SOURCES they won't be installed [1].

[1]: http://www.gnu.org/software/automake/manual/automake.html#Headers

Change-Id: I14fd816294682e7bd0fccefd6428e1526c9470d8
2011-11-11 16:29:32 -08:00
964387ed19 use WEBP_INLINE for inline function declarations
removes a #define inline, objectionable in certain projects

Change-Id: Iebe0ce0b25a030756304d402679ef769e5f854d1
2011-11-11 10:53:58 -08:00
90880a11b4 Merge "manpages: break long lines" 2011-11-09 19:12:32 -08:00
b5910895ed Merge "manpages: minor formatting updates" 2011-11-09 19:11:56 -08:00
4c451e4a34 Merge "Rectify the Chunk parsing logic." 2011-11-09 19:09:57 -08:00
04e84cf1f1 examples: slight cleanup
spelling/indent/condense a few lines
removal/restriction of extern "C" usage
add const where applicable

Change-Id: Idd49b3498b1d7fc391724b4cde63706022217cb0
2011-11-04 15:30:28 -07:00
099717ce92 manpages: break long lines
Change-Id: I3bea93d358e5c3b3f7dd3c91e433ab4f817d1cd8
2011-11-04 11:25:43 -07:00
1daf39bbbc manpages: minor formatting updates
escape '-'s
use consistent section names
webpmux.1:
 document -loop
 create subsections for each OPTION group

Change-Id: If41dc24d80e4a662024e82697d8d2cc4e194a35d
2011-11-04 11:20:59 -07:00
abd030b573 fix missing "(void)" in function signature
Change-Id: I7edfc36174b6d16df909594c2693e75a09e1ac13
2011-11-01 06:24:34 -07:00
f6a7d75885 remove useless test
Change-Id: I1b31842ddf719ddcc185a5d6a5032a20d5e593e2
2011-11-01 05:18:46 -07:00
f07b2138cd Rectify the Chunk parsing logic.
For odd-sized chunk-payload, there's one Byte padding at the end to make the
next chunk start at even-address. The chunk-skip logic needs to be updated to
handle such cases.

Change-Id: I8939ed07584a6195092fbe703eb0ea711fa5f153
2011-10-31 18:47:33 -07:00
b8634f7d8f webpmux: fix lib link order
fixes static link (--disable-shared)

Change-Id: Ife926179490954a1d966d2deb97dd366a9d0dacd
2011-10-28 18:11:00 -07:00
42c2e682e0 Fix missing coma (on uncompiled code)
Spotted by Jakub, Thanks!

Change-Id: Ic2509acc683690748089b831ee3d109ad2162dc2
2011-10-27 06:37:36 -07:00
d8329d41e3 Android.mk: add missing source files
Fell out of date with some of the dsp.c moves.
Fixes Issue #93.

Change-Id: I2fb4424dbb0bc31af236cef2bed5ee542622067b
2011-10-19 20:02:23 -07:00
13a54df529 Merge "More aggressive copy-edit; add TODO; validate HTML5" 2011-10-12 15:59:30 -07:00
868b96aef4 More aggressive copy-edit; add TODO; validate HTML5
Change-Id: I45e7fde3eb33067274b5d454451f1bf8785511fd
2011-10-12 15:54:02 -07:00
767afea250 configure: check for a symbol contained in libpng
rather than 'main'. this fixes mis-detection when cross compiling.

Change-Id: I9560ca4805ef9eea5bd7370aeef2dcf6ed293478
2011-10-07 19:44:03 -07:00
408b8918c1 Merge "Linewrap at 72 cols. Casual copy-edit." 2011-10-07 17:50:31 -07:00
3ae318c770 Merge "Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.)" 2011-10-07 16:07:41 -07:00
918eb2d822 Merge "Basic container doc source clean-up; fix lists and pseudocode blocks." 2011-10-07 15:15:36 -07:00
03bec9e0c0 Linewrap at 72 cols. Casual copy-edit.
Change-Id: Iebecff7eb0a64080eab456f903afacca178d9d59
2011-10-07 14:37:54 -07:00
2678d819a4 Restore (most) emphasis; add emphasis to normative
RFC 2119 terms (MUST, etc.)

Change-Id: Ia07a64dd4eadebfe9ba8a98a4eef613d1b5d614a
2011-10-07 14:31:43 -07:00
428674dad1 Basic container doc source clean-up; fix lists and pseudocode blocks.
Add some furniture (README, template).

Change-Id: Ida154a625fe7013a1a759d16f11fd7ccd867c25d
2011-10-07 14:28:18 -07:00
6a77d92835 Merge "Makefile.vc: cosmetics" 2011-10-05 12:16:24 -07:00
28c38e8c8f Merge "Makefile.vc: condense directory creation rules" 2011-10-05 12:14:06 -07:00
55be2cf878 Initial import of container spec document, from pdftotext transform.
new file:   doc/webp-container-spec.txt

Change-Id: I60b97d6f0219f0041c92b6d980cd8ebae8ae4ca5
2011-10-04 16:27:59 -07:00
a82a788ba0 Makefile.vc: cosmetics
- break long lines
- detab variable assignments
- sort object names

Change-Id: I33a210e617552a97c36ac3b1e255af4c7f2b0e35
2011-10-04 13:46:09 -07:00
c8f41ce54e Makefile.vc: condense directory creation rules
Change-Id: I39fb8278f17498998f8a9cf022a548e0735b1d11
2011-10-04 13:39:39 -07:00
2b877cd0c8 Some fixes to Makefile.vc to support the src\mux directory.
Change-Id: I5a4a3087032dc1d5f75750b2db2a3092d39f8fc0
2011-10-01 23:47:58 +02:00
3eb969b3ed Merge "Add Makefile.vc for Mux library & binary." 2011-09-30 03:55:11 -07:00
e78e971e98 Add Makefile.vc for Mux library & binary.
Add Makefile.vc for Windows platform to build Mux library and example
command line tool.

Change-Id: Ic9e2290feda0ae3e2f2fb3c5f8d46e79eada71a3
2011-09-30 16:05:33 +05:30
6aedde5891 Add manual for WebPMux tool.
This change adds manual page for the command-line tool webpmux.

Change-Id: I01aed259c9db4bc605433f528a7deff4f5292fbe
2011-09-30 14:04:54 +05:30
8a360d0a69 Merge "Added WebPMux Binary." 2011-09-30 00:43:27 -07:00
a4f32caef4 Added WebPMux Binary.
This change adds a command line tool (webpmux) which uses MUX library
for manipulating WebP Mux container. This tool can be used to create a
WebP container file and extract/strip relevant data from the container
file.

Change-Id: If17818239448a428703760747fc84f77586045e4
2011-09-30 12:46:29 +05:30
f3bf4c769f Added Mux Container Spec & README for MUX-API.
Added the Mux Container Spec (RIFF structure) for the features supported
(Color Profile, XMP Metadata, Animation & Tiling) via WebP Mux. Also
added README file with example psudo code for using the Mux API(s) and a
short description on webpmux (comand line tool, planned to be pushed out
in subsequent Git change).

Change-Id: I87c322cada89955bd758dd524a862a8cbc407541
2011-09-30 10:51:35 +05:30
9f761cfae9 Changed function signature for WebPMuxCreate
Changed function signature of method WebPMuxCreate and few other minor nits.
Header file has been re-organised to have declaration of set/get/Delete
methods for different use-cases (Metadata, ColorProfile etc) in one place
instead of declaring all Set methods together followed by Get & Delete.

Change-Id: I52f6dffd216b1c343423d55a5b45fa1b9b9c1347
2011-09-29 10:58:40 +05:30
5f31b5ec64 Merge "Add Mux library for manipulating WebP container." 2011-09-27 11:55:45 -07:00
2315785f2c Add Mux library for manipulating WebP container.
This change adds the WebP Mux library for manipulating the WebP Mux
Container. The library document and command line tool exhibiting the
usage of this libary will follow in subsequent Git change.

Change-Id: I4cba7dd12307483185ad5a68df33af6c36c154c8
2011-09-27 14:14:46 +05:30
120 changed files with 21260 additions and 4589 deletions

1
.gitattributes vendored
View File

@ -1,3 +1,4 @@
.gitattributes export-ignore .gitattributes export-ignore
.gitignore export-ignore .gitignore export-ignore
.mailmap export-ignore .mailmap export-ignore
*.pdf -text -diff

1
.gitignore vendored
View File

@ -18,5 +18,6 @@ Makefile
Makefile.in Makefile.in
examples/[cd]webp examples/[cd]webp
/output /output
/doc/output
*.idb *.idb
*.pdb *.pdb

View File

@ -1,9 +1,15 @@
Contributors: Contributors:
- James Zern (jzern at google dot com) - James Zern (jzern at google dot com)
- Jan Engelhardt (jengelh at medozas dot de) - Jan Engelhardt (jengelh at medozas dot de)
- Johann (johannkoenig at google dot com)
- Jyrki Alakuijala (jyrki at google dot com)
- Lou Quillio (louquillio at google dot com)
- Martin Olsson (mnemo at minimum dot se)
- Mikołaj Zalewski (mikolajz at google dot com) - Mikołaj Zalewski (mikolajz at google dot com)
- Noel Chromium (noel at chromium dot org)
- Pascal Massimino (pascal dot massimino at gmail dot com) - Pascal Massimino (pascal dot massimino at gmail dot com)
- Pierre Joye (pierre dot php at gmail dot com) - Pierre Joye (pierre dot php at gmail dot com)
- Scott LaVarnway (slavarnway at google dot com)
- Somnath Banerjee (somnath dot banerjee at gmail dot com) - Somnath Banerjee (somnath dot banerjee at gmail dot com)
- Urvang Joshi (urvang at google dot com) - Urvang Joshi (urvang at google dot com)
- Vikas Arora (vikasa at google dot com) - Vikas Arora (vikasa at google dot com)

View File

@ -2,46 +2,70 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
src/dec/alpha.c \ src/dec/alpha.c \
src/dec/dsp.c \ src/dec/buffer.c \
src/dec/frame.c \ src/dec/frame.c \
src/dec/idec.c \ src/dec/idec.c \
src/dec/layer.c \ src/dec/io.c \
src/dec/quant.c \ src/dec/layer.c \
src/dec/tree.c \ src/dec/quant.c \
src/dec/vp8.c \ src/dec/tree.c \
src/dec/webp.c \ src/dec/vp8.c \
src/dec/io.c \ src/dec/vp8l.c \
src/dec/buffer.c \ src/dec/webp.c \
src/dsp/yuv.c \ src/dsp/cpu.c \
src/dsp/upsampling.c \ src/dsp/dec.c \
src/dsp/cpu.c \ src/dsp/dec_sse2.c \
src/dsp/dec.c \ src/dsp/enc.c \
src/dsp/dec_neon.c \ src/dsp/enc_sse2.c \
src/dsp/enc.c \ src/dsp/lossless.c \
src/enc/alpha.c \ src/dsp/upsampling.c \
src/enc/analysis.c \ src/dsp/upsampling_sse2.c \
src/enc/config.c \ src/dsp/yuv.c \
src/enc/dsp.c \ src/enc/alpha.c \
src/enc/filter.c \ src/enc/analysis.c \
src/enc/frame.c \ src/enc/backward_references.c \
src/enc/iterator.c \ src/enc/config.c \
src/enc/layer.c \ src/enc/cost.c \
src/enc/picture.c \ src/enc/filter.c \
src/enc/quant.c \ src/enc/frame.c \
src/enc/syntax.c \ src/enc/histogram.c \
src/enc/tree.c \ src/enc/iterator.c \
src/enc/webpenc.c src/enc/layer.c \
src/utils/bit_reader.c \ src/enc/picture.c \
src/utils/bit_writer.c \ src/enc/quant.c \
src/utils/thread.c \ src/enc/syntax.c \
src/enc/tree.c \
src/enc/vp8l.c \
src/enc/webpenc.c \
src/utils/bit_reader.c \
src/utils/bit_writer.c \
src/utils/color_cache.c \
src/utils/filters.c \
src/utils/huffman.c \
src/utils/huffman_encode.c \
src/utils/quant_levels.c \
src/utils/rescaler.c \
src/utils/thread.c \
src/utils/utils.c \
LOCAL_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD -DWEBP_USE_THREAD \ LOCAL_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD \
-DWEBP_USE_THREAD \
-finline-functions -frename-registers -ffast-math \ -finline-functions -frename-registers -ffast-math \
-s -fomit-frame-pointer -Isrc/webp -s -fomit-frame-pointer -Isrc/webp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
# Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal
# instructions to be generated for armv7a code. Instead target the neon code
# specifically.
LOCAL_SRC_FILES += src/dsp/dec_neon.c.neon
endif
LOCAL_STATIC_LIBRARIES := cpufeatures
LOCAL_MODULE:= webp LOCAL_MODULE:= webp
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
$(call import-module,android/cpufeatures)

679
ChangeLog
View File

@ -1,3 +1,682 @@
c655380 dec/io.c: cosmetics
fe1958f RGBA4444: harmonize lossless/lossy alpha values
681cb30 fix RGBA4444 output w/fancy upsampling
f06c1d8 Merge "Alignment fix" into 0.2.0
f56e98f Alignment fix
6fe843b avoid rgb-premultiply if there's only trivial alpha values
528a11a fix the ARGB4444 premultiply arithmetic
a0a4885 Lossless decoder fix for a special transform order
62dd9bb Update encoding heuristic w.r.t palette colors.
6f4272b remove unused ApplyInverseTransform()
93bf0fa Update ChangeLog (v0.2.0-rc1)
5934fc5 update AUTHORS
014a711 update NEWS
43b0d61 add support for ARGB -> YUVA conversion for lossless decoder
33705ca bump version to 0.2.0
c40d7ef fix alpha-plane check + add extra checks
a06f802 MODE_YUVA: set alpha to opaque if the image has none
52a87dd Merge "silence one more warning" into 0.2.0
3b02309 silence one more warning
f94b04f move some RGB->YUV functions to yuv.h
4b71ba0 README: sync [cd]webp help output
c9ae57f man/dwebp.1: add links to output file format details
292ec5c quiet a few 'uninitialized' warnings
4af3f6c fix indentation
9b261bf remove the last NOT_HAVE_LOG2 instances
323dc4d remove use of log2(). Use VP8LFastLog2() instead.
8c515d5 Merge "harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc" into 0.2.0
d4b4bb0 Merge changes I46090628,I1a41b2ce into 0.2.0
bff34ac harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc
a3c063c Merge "extra size check for security" into 0.2.0
5e79630 Merge "WebPEncode: clear stats at the start of encode" into 0.2.0
f1edf62 Merge "rationalize use of color-cache" into 0.2.0
c193331 extra size check for security
906be65 rationalize use of color-cache
dd1c387 Add image-hint for low-color images.
4eb7aa6 Merge "WebPCheckMalloc() and WebPCheckCalloc():" into 0.2.0
80cc730 WebPCheckMalloc() and WebPCheckCalloc():
183cba8 check VP8LBitWriterInit return
cbfa9ee lossless: fix crash on user abort
256afef cwebp: exit immediately on version mismatch
475d87d WebPEncode: clear stats at the start of encode
a7cc729 fix type and conversion warnings
7d853d7 add stats for lossless
d39177b make QuantizeLevels() store the sum of squared error
5955cf5 replace x*155/100 by x*101581>>16
7d732f9 make QuantizeLevels() store the sum of squared error
e45a446 replace x*155/100 by x*101581>>16
159b75d cwebp output size consistency:
cbee59e Merge commit 'v0.1.99'
1889e9b dwebp: report -alpha option
3bc3f7c Merge "dwebp: add PAM output support" into 0.2.0
d919ed0 dwebp: add PAM output support
85e215d README/manpages/configure: update website link
c3a207b Update ChangeLog (v0.1.99)
d1fd782 Merge "add extra precision about default values and behaviour" into 0.2.0
efc826e add extra precision about default values and behaviour
9f29635 header/doc clean up
ff9fd1b Makefile.vc: fix webpmux.exe *-dynamic builds
8aacc7b remove INAM, ICOP, ... chunks from the test webp file.
2fc1301 harmonize authors as "Name (mail@address)"
4a9f37b Merge "update NEWS" into 0.2.0
7415ae1 makefile.unix: provide examples/webpmux target
ce82ced update NEWS
641e28e Merge "man/cwebp.1: wording, change the date" into 0.2.0
c37c23e README: cosmetics
3976dcd man/cwebp.1: wording, change the date
3e5bbe1 Merge "rename 'use_argb_input' to 'use_argb'" into 0.2.0
ce90847 Merge "add some padding bytes areas for later use" into 0.2.0
2390dab Merge "fixing the findings by Frederic Kayser to the bitstream spec" into 0.2.0
0275159 add a very crude progress report for lossless
a4b9b1c Remove some unused enum values.
dd10817 rename 'use_argb_input' to 'use_argb'
90516ae add some padding bytes areas for later use
d03b250 fixing the findings by Frederic Kayser to the bitstream spec
ce156af add missing ABI compatibility checks
9d45416 Merge "Doc: container spec text tweaks" into 0.2.0
4e2e0a8 Doc: container spec text tweaks
f7f16a2 add ABI compatibility check
2a77557 Merge "swig: add WebPEncodeLossless* wrappers" into 0.2.0
a3ec622 mux.h: remove '* const' from function parameters
31426eb encode.h: remove '* const' from function parameters
9838e5d decode.h: remove '* const' from function parameters
4972302 swig: add WebPEncodeLossless* wrappers
9ff00ca bump encoder/decoder versions
c2416c9 add lossless quick encoding functions to the public API
4c1f5d6 Merge "NEWS: mention decode_vp8.h is no longer installed" into 0.2.0
6cb2277 NEWS: mention decode_vp8.h is no longer installed
d5e5ad6 move decode_vp8.h from webp/ to dec/
8d3b04a Merge "header clean-up" into 0.2.0
02201c3 Merge "remove one malloc() by making color_cache non dynamic" into 0.2.0
d708ec1 Merge "move MIN/MAX_HISTO_BITS to format_constants.h" into 0.2.0
ab2da3e Merge "add a malloc() check" into 0.2.0
2d571bd add a malloc() check
7f0c178 remove one malloc() by making color_cache non dynamic
6569cd7 Merge "VP8LFillBitWindow: use 64-bit path for msvc x64 builds" into 0.2.0
23d34f3 header clean-up
2a3ab6f move MIN/MAX_HISTO_BITS to format_constants.h
985d3da Merge "shuffle variables in HashChainFindCopy" into 0.2.0
cdf885c shuffle variables in HashChainFindCopy
c3b014d Android.mk: add missing lossless files
8c1cc6b makefile.unix dist: explicitly name installed includes
7f4647e Merge "clarify the colorspace naming and byte ordering of decoded samples" into 0.2.0
cbf6972 clarify the colorspace naming and byte ordering of decoded samples
857650c Mux: Add WebPDataInit() and remove WebPImageInfo
ff771e7 don't install webp/decode_vp8.h
596dff7 VP8LFillBitWindow: use 64-bit path for msvc x64 builds
3ca7ce9 Merge "doc: remove non-finalized chunk references" into 0.2.0
1efaa5a Merge "bump versions" into 0.2.0
51fa13e Merge "README: update cwebp help output" into 0.2.0
12f9aed README: update cwebp help output
f0b5def bump versions
4c42a61 update AUTHORS
6431a1c doc: remove non-finalized chunk references
8130c4c Merge "build: remove libwebpmux from default targets/config"
23b4443 Merge "configure: broaden test for libpng-config"
85bff2c Merge "doc: correct lossless prefix coding table & code"
05108f6 Merge "More spec/code matching in mux:"
6808e69 More spec/code matching in mux:
bd2b46f Merge "doc/webp-container-spec: light cosmetics"
20ead32 doc/webp-container-spec: light cosmetics (full)
1d40a8b configure: add pthread detection
b5e9067 fix some int <-> size_t mix for buffer sizes
e41a759 build: remove libwebpmux from default targets/config
0fc2baa configure: broaden test for libpng-config
45b8272 Merge "restore authorship to lossless bitstream doc"
06ba059 restore authorship to lossless bitstream doc
44a09a3 add missing description of the alpha filtering methods
63db87d Merge "vwebp: add checkboard background for alpha display"
a73b897 vwebp: add checkboard background for alpha display
939158c Merge "vwebp: fix info display"
b35c07d vwebp: fix info display
48b39eb fix underflow for very short bitstreams
7e62298 cosmetics: param alignment, manpage wording
1bd7dd5 Merge changes I7b0afb0d,I7ecc9708
ac69e63 Merge "Updated cwebp man's help for Alpha & Lossless."
c0e8859 Get rid of image_info_ from WebPChunk struct.
135ca69 WebP Container Spec:
eb6f9b8 Updated cwebp man's help for Alpha & Lossless.
0fa844f cosmetic fixes on assert and 'const' where applicable
7f22bd2 check limit of width * height is 32 bits
16c46e8 autoconf/make: cosmetics: break long lines
ab22a07 configure: add helper macro to define --with-*
c17699b configure: add libtiff test
0e09732 Merge "cwebp: fix crash with yuv input + lossless"
88a510f Merge "fix big-endian VP8LWriteBits"
da99e3b Merge "Makefile.vc: split mux into separate lib"
7bda392 cwebp: fix crash with yuv input + lossless
f56a369 fix big-endian VP8LWriteBits
54169d6 Merge "cwebp: name InputFileFormat members consistently"
e2feefa Makefile.vc: split mux into separate lib
27caa5a Merge "cwebp: add basic TIFF support"
d8921dd cwebp: name InputFileFormat members consistently
6f76d24 cwebp: add basic TIFF support
4691407 Merge changes If39ab7f5,I3658b5ae
cca7c7b Fixed nit: 10 -> 10.f
5d09a24 WebPMuxCreate() error handling:
777341c Fix a memleak in WebPMuxCreate()
61c9d16 doc: correct lossless prefix coding table & code
4c39757 Merge "mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN"
e4e36cc Merge "Mux: Allow only some frames/tiles to have alpha."
ad2aad3 Merge "WebP Decoding error handling:"
97649c8 Mux: Allow only some frames/tiles to have alpha.
f864be3 Lower the quality settings for Alpha encoding.
3ba81bb WebP Decoding error handling:
fcc6992 add automatic YUVA/ARGB conversion during WebPEncode()
802e012 fix compilation in non-FANCY_UPSAMPLING mode
e012dfd make width/height coding match the spec
228d96a mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN
637a314 remove the now unused *KeepA variants
d11f6fc webpmux returns error strings rather than numbers
fcec059 makefile.unix: cwebp: fix OSX link
6b811f1 Merge "doc: remove lossless pdf"
c963482 doc: remove lossless pdf
b9ae4f0 cosmetics after mux changes b74ed6e, b494ad5
b494ad5 Mux: only allow adding frame/tiles at the end.
2c341b0 Merge "Added image characteristic hint for the codec."
d373076 Added image characteristic hint for the codec.
2ed2adb Merge "msvc: add intrinsic based BitsLog2Floor"
e595e7c Merge "add demux.c to the makefiles"
da47b5b Merge "demux: add {Next,Prev}Chunk"
e5f4674 add demux.c to the makefiles
4708393 demux: add {Next,Prev}Chunk
e8a0a82 demux: quiet msvc warnings
7f8472a Update the WebP Container Spec.
31b68fe cleanup WebPPicture struct and API
9144a18 add overflow check before calling malloc()
81720c9 consistency cosmetics
2ebe839 Merge "Add kramdown version information to README"
7144308 enc/vp8l.c: fix build
b7ac19f Add kramdown version information to README
efdcb66 Merge "Edit for consistency, usage and grammar."
0822010 Enable alpha in vvwebp
8de9a08 Merge "Mux API change:"
b74ed6e Mux API change:
233a589 take picture->argb_stride into account for lossless coding
04e33f1 Edit for consistency, usage and grammar.
a575b4b Merge "cosmetics: add missing const"
8d99b0f Merge "cosmetics: remove unimplemented function proto"
69d0221 cosmetics: add missing const
5b08318 cosmetics: remove unimplemented function proto
b7fb0ed Log warning for unsupported options for lossless.
e1f769f msvc: add intrinsic based BitsLog2Floor
8a69c7d Bug-fix: Clamp backward dist to 1.
b5b6ac9 Merge "Bring the special writer 'WebPMemoryWriter' to public API"
a6a1909 Merge "Fix floating point exception with cwebp -progress"
f2cee06 Fix floating point exception with cwebp -progress
91b7a8c Bring the special writer 'WebPMemoryWriter' to public API
310e297 support resize and crop for RGBA input
a89835d Merge changes Ice662960,Ie8d7aa90,I2d996d5e,I01c04772
ce614c0 Merge "dec/vp8: avoid setting decoder status twice"
900285d dec/vp8: avoid setting decoder status twice
8227adc Merge changes I6f02b0d0,I5cbc9c0a,I9dd9d4ed,Id684d2a1
dcda59c Merge "demux: rename SetTile to SelectTile"
622ef12 demux: rename SetTile to SelectTile
81ebd37 Merge "demux: add {Next,Prev}Frame"
02dd37a demux: add {Next,Prev}Frame
4b79fa5 Merge "Limit the maximum size of huffman Image to 16MB."
9aa34b3 Manually number "chapters," as chapter numbers are used in the narrative.
2a4c6c2 Re-wrap at <= 72 columns
a45adc1 Apply inline emphasis and monospacing, per gdoc / PDF
9101120 Incorporate gdoc changes through 2012-06-08
7a18248 Removed CodeRay syntax declarations ...
b3ec18c Provide for code-block syntax highlighting.
709d770 Replace high ASCII artifacts (curly quotes, etc.).
930e8ab Lossless WebP doc largely ported to markdown text.
18cae37 msvc: silence some build warnings
b392308 Limit the maximum size of huffman Image to 16MB.
f180df2 Merge "libwebp/demux: add Frame/Chunk iteration"
2bbe1c9 Merge "Enable lossless encoder code"
d0601b0 Merge changes I1d97a633,I81c59093
78f3e34 Enable lossless encoder code
d974a9c Merge "libwebp/demux: add simple format parsing"
26bf223 Merge "libwebp: add WebPDemux stub functions"
2f66668 Merge "modify WebPParseHeaders to allow reuse by GetFeatures"
b402b1f libwebp/demux: add Frame/Chunk iteration
ad9ada3 libwebp/demux: add WebPDemuxGetI
2f2d4d5 libwebp/demux: add extended format parsing
962dcef libwebp/demux: add simple format parsing
f8f9408 libwebp: add WebPDemux stub functions
fb47bb5 Merge "NumNamedElements() should take an enum param."
7c68980 Fix asserts in Palette and BackwardReference code.
fbdcb7e NumNamedElements() should take an enum param.
fb4943b modify WebPParseHeaders to allow reuse by GetFeatures (old-decode-alph-3)
3697b5c write an ad-hoc EncodeImageInternal variant
eaee9e7 Bug-Fix: Decode small (less than 32 bytes) images.
0bceae4 Merge "cwebp: fix alpha reporting in stats output"
0424b1e Rebase default encoding settings.
c71ff9e cwebp: fix alpha reporting in stats output
e2ffe44 Merge "Stop indefinite recursion for Huffman Image."
70eb2bd Stop indefinite recursion for Huffman Image.
f3bab8e Update vwebp
6d5c797 Remove support for partial files in Mux.
f1df558 WebPMuxAssemble() returns WebPData*.
814a063 Rename 'Add' APIs to 'Set'.
bbb0218 Update Mux psuedo-code examples.
4fc4a47 Use WebPData in MUX set APIs
c67bc97 Merge "add WebPPictureImportRGBX() and WebPPictureImportBGRX()"
27519bc add WebPPictureImportRGBX() and WebPPictureImportBGRX()
f80cd27 factorize code in Import()
9b71502 histogram: add log2 wrapper
8c34378 Merge "fix some implicit type conversion warnings"
42f6df9 fix some implicit type conversion warnings
250c16e Merge "doc: update lossless pdf"
9d9daba Merge "add a PDF of the lossless spec"
8fbb918 prefer webp/types.h over stdint.h
0ca170c doc: update lossless pdf
0862ac6 add a PDF of the lossless spec
437999f introduce a generic WebPPictureHasTransparency() function
d2b6c6c cosmetic fixes after Idaba281a
b4e6645 Merge "add colorspace for premultiplied alpha"
48f8275 add colorspace for premultiplied alpha
069f903 Change in lossless bit-stream.
5f7bb3f Merge "WebPReportProgress: use non-encoder specific params"
f18281f WebPReportProgress: use non-encoder specific params
9ef3228 Add support for raw lossless bitstream in decoder.
7cbee29 Fix bug: InitIo reseting fancy_upsampling flag.
880fd98 vwebp: fix exit w/freeglut
1875d92 trap two unchecked error conditions
87b4a90 no need to have mux.h as noinst clause in enc/
88f41ec doc: fix bit alignment in VP8X chunk
52f5a4e Merge "fix bug with lossy-alpha output stride"
3bde22d fix bug with lossy-alpha output stride
42d61b6 update the spec for the lossy-alpha compression methods.
e75dc80 Move some more defines to format_constants.h
c13f663 Move consts to internal header format_constants.h
7f2dfc9 use a bit-set transforms_seen_ instead of looping
18da1f5 modulate alpha-compression effort according to config.method
f5f2fff Merge "Alpha flag fix for lossless."
c975c44 Alpha flag fix for lossless.
4f067fb Merge "Android: only build dec_neon with NEON support"
255c66b Android: only build dec_neon with NEON support
8f9117a cosmetics: signature fixes
39bf5d6 use header-less lossless bitstream for alpha channel
75d7f3b Merge "make input data be 'const' for VP8LInverseTransform()"
9a721c6 make input data be 'const' for VP8LInverseTransform()
9fc64ed Disallow re-use of same transformation.
98ec717 use a function pointer for ProcessRows()
f7ae5e3 cosmetics: join line
140b89a factor out buffer alloc in AllocateARGBBuffers()
a107dfa Rectify WebPParseOptionalChunks().
237eab6 Add two more color-spaces for lossless decoding.
27f417a fix orthographic typo
489ec33 add VP8LEncodeStream() to compress lossless image stream
fa8bc3d make WebPEncodingSetError() take a const picture
638528c bitstream update for lossy alpha compression
d73e63a add DequantizeLevels() placeholder
ec122e0 remove arch-dependent rand()
d40e765 fix alignment
1dd6a8b Merge "remove tcoder, switch alpha-plane compression to lossless"
3e863dd remove tcoder, switch alpha-plane compression to lossless
8d77dc2 Add support for lossless in mux:
831bd13 Make tile size a function of encoding method.
778c522 Merge "remove some variable shadowing"
817c9dc Few more HuffmanTreeToken conversions.
37a77a6 remove some variable shadowing
89c07c9 Merge "normalize example header includes"
4aff411 Merge "add example_util.[hc]"
00b29e2 normalize example header includes
061263a add example_util.[hc]
c6882c4 merge all tree processing into a single VP8LProcessTree()
9c7a3cf fix VP8LHistogramNumCodes to handle the case palette_code_bits == 0
b5551d2 Merge "Added HuffmanTreeCode Struct for tree codes."
8b85d01 Added HuffmanTreeCode Struct for tree codes.
093f76d Merge "Allocate single memory in GetHuffBitLengthsAndCodes."
41d8049 Allocate single memory in GetHuffBitLengthsAndCodes.
1b04f6d Correct size in VP8L header.
2924a5a Makefile.vc: split object lists based on directory
c8f2416 Merge "add assert(tokens)"
4323994 add assert(tokens)
9f54745 Catch an error in DecodeImageData().
ac8e5e4 minor typo and style fix
9f566d1 clean-up around Huffman-encode
c579a71 Introduce CHUNK_SIZE_BYTES in muxi.h.
14757f8 Make sure huffman trees always have valid symbols
4105061 makefile.unix: add support for building vwebp
48b3772 Merge "fixed signed/unsigned comparison warning"
57f696d Merge "EncodeImageInternal: fix potential leak"
d972cdf EncodeImageInternal: fix potential leak
5cd12c3 fixed signed/unsigned comparison warning
cdca30d Merge "cosmetics: shorten long line"
e025fb5 cosmetics: shorten long line
22671ed Merge "enc/vp8l: fix double free on error"
e1b9b05 Merge "cosmetics: VP8LCreateHuffmanTree: fix indent"
a8e725f enc/vp8l: fix double free on error
27541fb cosmetics: VP8LCreateHuffmanTree: fix indent
1d38b25 cwebp/windows: use MAKE_REFGUID where appropriate
817ef6e Merge "cwebp: fix WIC/Microsoft SDK compatibility issue"
902d3e3 cwebp: fix WIC/Microsoft SDK compatibility issue
89d803c Merge "Fix a crash due to wrong pointer-integer arithmetic."
cb1bd74 Merge "Fix a crash in lossless decoder."
de2fe20 Merge "Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool' outside the loop. - Some renames/tweaks for readability."
ce69177 Fix a crash due to wrong pointer-integer arithmetic.
e40a368 Fix a crash in lossless decoder.
3927ff3 remove unneeded error condition for WebPMuxNumNamedElements()
2c140e1 Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool' outside the loop. - Some renames/tweaks for readability.
861a5b7 add support for animation
eb5c16c Merge "Set correct encode size in encoder's stats."
4abe04a fix the return value and handle missing input file case.
2fafb85 Set correct encode size in encoder's stats.
e7167a2 Provide one entry point for backward references.
c4ccab6 Print relevant lossless encoding stats in cwebp.
e3302cf GetHuffBitLengthsAndCodes: reduce level of indirection
b5f2a9e enc/vp8l: fix uninitialized variable warning
7885f8b makefile.unix: add lossless encoder files
1261a4c Merge "cosmetics"
3926b5b Merge "dsp/cpu.c: Android: fix crash on non-neon arm builds"
834f937 dsp/cpu.c: Android: fix crash on non-neon arm builds
126e160 cosmetics
e38602d Merge branch 'lossless_encoder'
e8d3d6a split StoreHuffmanCode() into smaller functions
d0d8899 more consolidation: introduce VP8LHistogramSet
1a210ef big code clean-up and refactoring and optimization
41b5c8f Some cosmetics in histogram.c
ada6ff7 Approximate FastLog between value range [256, 8192]
ec123ca Forgot to update out_bit_costs to symbol_bit_costs at one instance.
cf33ccd Evaluate output cluster's bit_costs once in HistogramRefine.
781c01f Simple Huffman code changes.
a2849bc Lossless decoder: remove an unneeded param in ReadHuffmanCodeLengths().
b39e748 Reducing emerging palette size from 11 to 9 bits.
bfc73db Move GetHistImageSymbols to histogram.c
889a578 Improve predict vs no-predict heuristic.
01f5066 code-moving and clean-up
31035f3 reduce memory usage by allocating only one histo
fbb501b Restrict histo_bits to ensure histo_image size is under 32MB
8415ddf further simplification for the meta-Huffman coding
e491729 A quick pass of cleanup in backward reference code
83332b3 Make transform bits a function of encode method (-m).
72920ca introduce -lossless option, protected by USE_LOSSLESS_ENCODER
c6ac4df Run TraceBackwards for higher qualities.
412222c Make histo_bits and transform_bits function of quality.
149b509 Update lossless encoder strategy:
0e6fa06 cache_bits passed to EncodeImageInternal()
e38b40a Factorize code for clearing HtreeGroup.
6f4a16e Removing the indirection of meta-huffman tables.
3d33ecd Some renaming/comments related to palette in lossless encoder.
4d02d58 Lossless encoder: correction in Palette storage
4a63623 fix a memleak in EncodeImageInternal()
0993a61 Full and final fix for prediction transform
afd2102 Fix cross-color transform in lossless encoder
b96d874 Need to write a '0' bit at the end of transforms.
54dad7e Color cache size should be counted as 0 when cache bits = 0
4f0c5ca Fix prediction transform in lossless encoder.
36dabda Fix memory leak in method EncodeImageInternal for histogram_image.
352a4f4 Get rid of PackLiteralBitLengths()
d673b6b Change the predictor function to pass left pixel
b2f9946 Fix CopyTileWithPrediction()
84547f5 Add EncodeImageInternal() method.
6b38378 Guard the lossless encoder (in flux) under a flag
09f7532 Fix few nits (const qualifiers)
648be39 Added implementation for various lossless functions
32714ce Add VP8L prefix to backward ref & histogram methods.
fcba7be Fixed header file tag (WEBP_UTILS_HUFFMAN_ENCODE_H_)
bc70374 Add backward_ref, histogram & huffman encode modules from lossless.
fdccaad Fixing nits
227110c libwebp interface changes for lossless encoding.
50679ac minor style fixes
b38dfcc remove unneeded reference to NUM_LITERAL_CODES
8979675 harmonize header description
c04eb7b tcoder.c: define NOT_HAVE_LOG2 for MSVC builds
9a214fa Merge "VP8[L]GetInfo: check input pointers"
5c5be8b VP8[L]GetInfo: check input pointers
0c188fe Merge changes I431acdfe,I713659b7
b3515c6 mux: drop 'chunk' from ChunkInfo member names
aea7923 muxi.h: remove some unused defines
0142249 update NEWS file for next release
29e3f7e Merge "dec: remove deprecated WebPINew()"
4718e44 Merge "muxedit: a few more size_t changes"
82654f9 Merge "muxedit: remove a few redundant NULL checks"
02f27fb dec: remove deprecated WebPINew()
ccddb3f muxedit: remove a few redundant NULL checks
a6cdf71 muxedit: a few more size_t changes
a384689 Merge "mux: remove unused LIST_ID"
11ae46a alpha.c: quiet some size_t -> int conversion warnings
dee4669 mux: remove unused LIST_ID
03f1f49 mux: add version checked entry points
6a0abda Merge "doc: tile/alpha corrections"
c8139fb Merge "few cosmetics"
6833873 Merge "lossless: remove some size_t -> int conversions"
5249e94 doc: tile/alpha corrections
d96e722 huffman: quiet int64 -> int conversion warning
532020f lossless: remove some size_t -> int conversions
23be6ed few cosmetics
1349eda Merge "configure: AC_ARG_* use AS_HELP_STRING"
bfbcc60 configure: AC_ARG_* use AS_HELP_STRING
1427ca8 Merge "Makefile.am: header file maintenance"
087332e Merge "remove unused parameter 'round' from CalcProba()"
9630e16 remove unused parameter 'round' from CalcProba()
92092ea Merge "bit_reader.h: correct include"
a87fc3f Merge "mux: ensure # images = # tiles"
53af99b Merge "mux: use size_t consistently"
39a57da Makefile.am: header file maintenance
1bd0bd0 bit_reader.h: correct include
326a3c6 mux: ensure # images = # tiles
95667b8 mux: use size_t consistently
231ec1f Removing the indirection of meta-huffman tables.
15ebcba check return pointer from MuxImageGetListFromId
b0d6c4a Merge "configure: remove test for zlib.h"
8cccac5 Merge "dsp/lossless: silence some build warnings"
b08819a dsp/lossless: silence some build warnings
7ae2252 Android.mk: SSE2 & NEON updates
0a49e3f Merge "makefile.unix add missing header files"
2e75a9a Merge "decode.h: use size_t consistently"
fa13035 configure: remove test for zlib.h
d3adc81 makefile.unix add missing header files
262fe01 Merge "makefile.unix & Android.mk: cosmetics"
4cce137 Merge "enc_sse2 add missing stdlib.h include"
80256b8 enc_sse2 add missing stdlib.h include
9b3d1f3 decode.h: use size_t consistently
64083d3 Merge "Makefile.am: cosmetics"
dceb8b4 Merge changes If1331d3c,I86fe3847
0e33d7b Merge "webp/decode.h: fix prototypes"
fac0f12 rename BitReader to VP8LBitReader
fbd82b5 types.h: centralize use of stddef.h
2154835 Makefile.am: cosmetics
1c92bd3 vp8io: use size_t for buffer size
90ead71 fix some more uint32_t -> size_t typing
cbe705c webp/decode.h: fix prototypes
3f8ec1c makefile.unix & Android.mk: cosmetics
217ec7f Remove tabs in configure.ac
b3d35fc Merge "Android.mk & Makefile.vc: add new files"
0df04b9 Android.mk & Makefile.vc: add new files
e4f20c5 Merge "automake: replace 'silent-rules' w/AM_SILENT_RULES"
8d254a0 cosmetics
6860c2e fix some uint32_t -> size_t typing
4af1858 Fix a crash due to max symbol in a tree >= alphabet size
6f01b83 split the VP8 and VP8L decoding properly
f2623db enable lossless decoder
b96efd7 add dec/vp8i.h changes from experimental
19f6398 add dec/vp8l{i.h,.c} from experimental
c4ae53c add utils/bit_reader.[hc] changes from experimental
514d008 add dsp/lossless.[hc] from experimental
9c67291 add utils/huffman.[hc] from experimental
337914a add utils/color_cache.[hc] from experimental
b3bf8fe the read-overflow code-path wasn't reporting as an error
1db888b take colorspace into account when cropping
61c2d51 move the rescaling code into its own file and make enc/ and dec/ use it.
efc2016 Make rescaler methods generic
3eacee8 Move rescaler methods out of io.c.
a69b893 automake: replace 'silent-rules' w/AM_SILENT_RULES
6f7bf64 issue 111: fix little-endian problem in bit-reader
ed278e2 Removed unnecessary lookup
cd8c3ba fix some warnings: down-cast and possibly-uninitialized variable
0a7102b ~1% improvement of alpha compression
3bc1b14 Merge "Reformat container doc"
dc17abd mux: cosmetics
cb5810d Merge "WebPMuxGetImage: allow image param to be NULL"
506a4af mux: cosmetics
135e8b1 WebPMuxGetImage: allow image param to be NULL
de556b6 Merge "README.mux: reword some descriptions"
0ee2aeb Makefile.vc: use batch mode rules
d9acddc msvc: move {i,p}db creation to object directory
237c9aa Merge "expose WebPFree function for DLL builds"
b3e4054 silence msvc debug build warning
45feb55 expose WebPFree function for DLL builds
11316d8 README.mux: reword some descriptions
4be52f4 factorize WebPMuxValidate
14f6b9f mux: light cleanup
5e96a5d add more param checks to WebPPictureDistortion()
8abaf82 Merge "silence some type size related warnings"
1601a39 silence some type size related warnings
f3abe52 Merge "idec: simplify buffer size calculation"
a9c5cd4 idec: simplify buffer size calculation
7b06bd7 Merge "configure/automake: add silent-rules option"
e9a7d14 Reformat container doc
d4e5c7f configure/automake: add silent-rules option
5081db7 configure/automake: no -version-info for convenience libs
85b6ff6 Merge "idec: fix WebPIUpdate failure"
7bb6a9c idec: fix internal state corruption
89cd1bb idec: fix WebPIUpdate failure
01b6380 4-5% faster decoding, optimized byte loads in arithmetic decoder.
631117e Merge "cosmetics & warnings"
a0b2736 cosmetics & warnings
f73947f use 32bit for storing dequant coeffs, instead of 16b.
b960030 Merge "store prediction mode array as uint8_t[16], not int[16]."
7b67881 store prediction mode array as uint8_t[16], not int[16].
cab8d4d Merge "NEON TransformOne"
ba503fd NEON TransformOne
9f740e3 Merge "gcc warning fix: remove the 'const' qualifier."
f76d358 gcc warning fix: remove the 'const' qualifier.
e78478d Merge "webpmux: make more use of WebPData"
f85bba3 Merge "manpages: add BUGS section"
48a43bb Merge "makefile.unix: variable cosmetics"
c274dc9 makefile.unix: variable cosmetics
1f7b859 re-organize the error-handling in the main loop a bit
1336fa7 Only recompute level_cost_[] when needed
771ee44 manpages: add BUGS section
0f7820e webpmux: make more use of WebPData
974aaff examples: logging updates
6c14aad Merge "better token buffer code"
f405425 better token buffer code
18d959f Merge "mux: add WebPData type"
eec4b87 mux: add WebPData type
0de3096 use 16bit counters for recording proba counts
7f23678 fix for LevelCost + little speed-up
7107d54 further speed-up/cleanup of RecordCoeffs() and GetResidualCost()
fd22104 Introduce Token buffer (unused for now)
5fa148f Merge "speed-up GetResidualCost()"
28a9d9b speed-up GetResidualCost()
11e7dad Merge "misc cosmetics"
378086b misc cosmetics
d61479f add -print_psnr and -print_ssim options to cwebp.
2e3e8b2 add a WebPCleanupTransparentArea() method
552c121 Merge "mux: plug some memory leaks on error"
a2a81f7 Merge "fix Mach-O shared library build"
b3482c4 Merge "fix gcc-4.0 apple 32-bit build"
e4e3ec1 fix gcc-4.0 apple 32-bit build
b0d2fec mux: plug some memory leaks on error
f0d2c7a pass of cosmetics
b309a6f fix Mach-O shared library build
241ddd3 doc: delete mux container pdf
8b1ba27 doc: update VP8 decode guide link
7e4371c WebPMuxCreate: fix unchecked malloc
eb42558 Merge "have makefile.unix clean up src/webp/*~ too"
a85c363 Merge "correct EncodeAlpha documentation"
a33842f Merge "Update webp container spec with alpha filter options."
8d6490d Incremental support for some of the mux APIs.
b8375ab have makefile.unix clean up src/webp/*~ too
b5855fc correct EncodeAlpha documentation
dba37fe Update webp container spec with alpha filter options.
2e74ec8 fix compile under MINGW
716d1d7 fix suboptimal MAX_LEN cut-off limit
57cab7b Harmonize the alpha-filter predictions at boundary
3a98953 Merge "Fix bug for Alpha in RGBA_4444 color-mode."
8ca2076 Introduce a 'fast' alpha mode
221a06b Fix bug for Alpha in RGBA_4444 color-mode.
ad1e163 cosmetics: normalize copyright headers
c77424d cosmetics: light include cleanup
9d0e17c fix msvc build breakage after 252028a
7c4c177 Some readability fixes for mux library
d8a47e6 Merge "Add predictive filtering option for Alpha."
252028a Add predictive filtering option for Alpha.
9b69be1 Merge "Simplify mux library code"
a056170 Simplify mux library code
992187a improve log2 test
e852f83 update Android.mk file list
a90cb2b reduce number of copies and mallocs in alpha plane enc/dec
b1662b0 fix some more type conversion warnings w/MSVC
223d8c6 fix some uint64_t -> int conversion warnings with MSC
c1a0437 Merge "simplify checks for enabling SSE2 code"
f06817a simplify checks for enabling SSE2 code
948d4fe silence a msvc build warning
9117954 vwebp: msvc build tweaks
7937b40 simple WebP viewer, based on OpenGL
6aac1df add a bunch of missing 'extern "C"'
421eb99 Merge "Remove assigned-but-not-used variable "br""
91e27f4 better fitting names for upsampling functions
a5d7ed5 Remove assigned-but-not-used variable "br"
f62d2c9 remove unused 'has_alpha' from VP8GetInfo() signature
08e8658 trap alpha-decoding error
b361eca add cut-off to arith coder probability update.
8666a93 Some bug-fixes for images with alpha.
273a12a fix off-by-1 diff in case cropping and simple filtering
2f741d1 webpmux: ReadImage: fix ptr free in error case
721f3f4 fix alpha decode
60942c8 fix the has_alpha_ order
30971c9 Implement progress report (and user abort)
eda520a cosmetics after 9523f2a
38bd5bb Merge "Better alpha support in webpmux binary"
ccbaebf Merge "Updated the includes to relative paths."
d71fbdc fix small typo in error message array
cdf97aa Better alpha support in webpmux binary
885f25b Updated the includes to relative paths.
a0ec9aa Update WebP encoder (cwebp) to support Alpha.
667b769 Fixed the include for types.h within mux.h
9523f2a Add Alpha Encode support from WebPEncode.
16612dd Merge "Add Alpha Decode support from WebPDecode."
d117a94 Add Alpha Decode support from WebPDecode.
6722873 cosmetics after e1947a9
e1947a9 Add Alpha encode/decode code.
afc4c5d simplify code by introducing a CopyPlane() helper func
113b312 Merge "MUX API Updates"
c398f59 MUX API Updates
5acf04e remove orphan source file
059f03e Merge "dec: validate colorspace before using as array index"
70a0398 Merge "factorize some code"
9b243b3 factorize some code
372e2b4 Correct a bug in ReadPNG() with GRAY_ALPHA images
469d6eb Merge "Makefile.am: remove redundant noinst_HEADERS"
9fe3372 dec: validate colorspace before using as array index
8962030 remove orphan source file
ced3e3f Makefile.am: remove redundant noinst_HEADERS
964387e use WEBP_INLINE for inline function declarations
90880a1 Merge "manpages: break long lines"
b591089 Merge "manpages: minor formatting updates"
4c451e4 Merge "Rectify the Chunk parsing logic."
04e84cf examples: slight cleanup
099717c manpages: break long lines
1daf39b manpages: minor formatting updates
abd030b fix missing "(void)" in function signature
f6a7d75 remove useless test
f07b213 Rectify the Chunk parsing logic.
b8634f7 webpmux: fix lib link order
42c2e68 Fix missing coma (on uncompiled code)
d8329d4 Android.mk: add missing source files
13a54df Merge "More aggressive copy-edit; add TODO; validate HTML5"
868b96a More aggressive copy-edit; add TODO; validate HTML5
767afea configure: check for a symbol contained in libpng
408b891 Merge "Linewrap at 72 cols. Casual copy-edit."
3ae318c Merge "Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.)"
918eb2d Merge "Basic container doc source clean-up; fix lists and pseudocode blocks."
03bec9e Linewrap at 72 cols. Casual copy-edit.
2678d81 Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.)
428674d Basic container doc source clean-up; fix lists and pseudocode blocks.
6a77d92 Merge "Makefile.vc: cosmetics"
28c38e8 Merge "Makefile.vc: condense directory creation rules"
55be2cf Initial import of container spec document, from pdftotext transform.
a82a788 Makefile.vc: cosmetics
c8f41ce Makefile.vc: condense directory creation rules
2b877cd Some fixes to Makefile.vc to support the src\mux directory.
3eb969b Merge "Add Makefile.vc for Mux library & binary."
e78e971 Add Makefile.vc for Mux library & binary.
6aedde5 Add manual for WebPMux tool.
8a360d0 Merge "Added WebPMux Binary."
a4f32ca Added WebPMux Binary.
f3bf4c7 Added Mux Container Spec & README for MUX-API.
9f761cf Changed function signature for WebPMuxCreate
5f31b5e Merge "Add Mux library for manipulating WebP container."
2315785 Add Mux library for manipulating WebP container.
7e198ab update ChangeLog (v0.1.3)
dfc9c1e Harmonize the dates dfc9c1e Harmonize the dates
28ad70c Fix PNG decoding bug 28ad70c Fix PNG decoding bug
846e93c Update AUTHORS & add .mailmap 846e93c Update AUTHORS & add .mailmap

View File

@ -1,18 +1,8 @@
# #
# Stem for static libs and DLLs # Stem for static libs and DLLs
# #
LIB_NAME = libwebp_a LIBWEBP_BASENAME = libwebp
LIB_NAME_DEBUG = libwebp_a_debug LIBWEBPMUX_BASENAME = libwebpmux
#
# Stem for DLL import libs
#
IMPLIB_NAME = libwebp_dll
IMPLIB_NAME_DEBUG = libwebp_dll_debug
!IFNDEF DEP_PATH
DEPS_PATH = ../../deps
!ENDIF
!IFNDEF ARCH !IFNDEF ARCH
!IF ! [ cl 2>&1 | find "x86" > NUL ] !IF ! [ cl 2>&1 | find "x86" > NUL ]
@ -38,7 +28,8 @@ CCDEBUG = cl.exe /nologo /Od /Gm /Zi /D_DEBUG /RTC1
CFLAGS = /Isrc /nologo /W3 /EHsc /FD /c /GS CFLAGS = /Isrc /nologo /W3 /EHsc /FD /c /GS
CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD
LDFLAGS = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE $(PLATFORM_LDFLAGS) LDFLAGS = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE
LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS)
LNKDLL = link.exe /DLL LNKDLL = link.exe /DLL
LNKLIB = link.exe /lib LNKLIB = link.exe /lib
LNKEXE = link.exe LNKEXE = link.exe
@ -65,36 +56,49 @@ DIROBJ = $(DIRBASE)\obj
DIRLIB = $(DIRBASE)\lib DIRLIB = $(DIRBASE)\lib
DIRINC = $(DIRBASE)\include DIRINC = $(DIRBASE)\include
DIRBIN = $(DIRBASE)\bin DIRBIN = $(DIRBASE)\bin
LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME).pdb
OUTPUT_DIRS = $(DIRBIN) $(DIRINC) $(DIRLIB) \
$(DIROBJ)\dec \
$(DIROBJ)\dsp \
$(DIROBJ)\enc \
$(DIROBJ)\examples \
$(DIROBJ)\mux \
$(DIROBJ)\utils \
# Target configuration # Target configuration
!IF "$(CFG)" == "release-static" !IF "$(CFG)" == "release-static"
TARGET = $(LIB_NAME).lib
CC = $(CCNODBG) CC = $(CCNODBG)
STATICLIBBUILD = TRUE STATICLIBBUILD = TRUE
!ELSE IF "$(CFG)" == "debug-static" !ELSE IF "$(CFG)" == "debug-static"
TARGET = $(LIB_NAME_DEBUG).lib
CC = $(CCDEBUG) CC = $(CCDEBUG)
RTLIB = $(RTLIBD)
STATICLIBBUILD = TRUE STATICLIBBUILD = TRUE
LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug
LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
!ELSE IF "$(CFG)" == "release-dynamic" !ELSE IF "$(CFG)" == "release-dynamic"
TARGETDLL = $(LIB_NAME).dll
TARGET = $(IMPLIB_NAME).lib
CC = $(CCNODBG) CC = $(CCNODBG)
DLLBUILD = TRUE DLLBUILD = TRUE
!ELSE IF "$(CFG)" == "debug-dynamic" !ELSE IF "$(CFG)" == "debug-dynamic"
TARGETDLL = $(LIB_NAME_DEBUG).dll
TARGET = $(IMPLIB_NAME_DEBUG).lib
CC = $(CCDEBUG) CC = $(CCDEBUG)
RTLIB = $(RTLIBD)
DLLBUILD = TRUE DLLBUILD = TRUE
LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug
LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
!ENDIF !ENDIF
!IF "$(STATICLIBBUILD)" == "TRUE" !IF "$(STATICLIBBUILD)" == "TRUE"
CC = $(CC) $(RTLIB) CC = $(CC) $(RTLIB)
LNK = $(LNKLIB) /out:$(DIRLIB)\$(TARGET)
CFGSET = TRUE CFGSET = TRUE
LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME).lib
LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME).lib
!ELSE IF "$(DLLBUILD)" == "TRUE" !ELSE IF "$(DLLBUILD)" == "TRUE"
DLLC = webp_dll.c
DLLINC = webp_dll.h DLLINC = webp_dll.h
CC = $(CC) /I$(DIROBJ) /FI$(DLLINC) $(RTLIB) /DWEBP_DLL CC = $(CC) /I$(DIROBJ) /FI$(DLLINC) $(RTLIB) /DWEBP_DLL
LNK = $(LNKDLL) /out:$(DIRBIN)\$(TARGETDLL) /implib:$(DIRLIB)\$(TARGET) LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME)_dll.lib
LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME)_dll.lib
LIBWEBP_OBJS = $(DIROBJ)\$(DLLC:.c=.obj)
LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME)_dll.pdb
CFGSET = TRUE CFGSET = TRUE
!ENDIF !ENDIF
@ -102,21 +106,24 @@ CFGSET = TRUE
# Usage # Usage
# #
!IF "$(CFGSET)" == "FALSE" !IF "$(CFGSET)" == "FALSE"
!MESSAGE Usage: nmake /f Makefile.vc [CFG=<config>] [OBJDIR=<path>] [RTLIBCFG=<rtlib>] [<target>] !MESSAGE Usage: nmake /f Makefile.vc [CFG=<config>]
!MESSAGE . [OBJDIR=<path>] [RTLIBCFG=<rtlib>] [<target>]
!MESSAGE
!MESSAGE where <config> is one of: !MESSAGE where <config> is one of:
!MESSAGE - release-static - release static library !MESSAGE - release-static - release static library
!MESSAGE - debug-static - debug static library !MESSAGE - debug-static - debug static library
!MESSAGE - release-dynamic - release dynamic link library (DLL) !MESSAGE - release-dynamic - release dynamic link library (DLL)
!MESSAGE - debug-dynamic - debug dynamic link library (DLL) !MESSAGE - debug-dynamic - debug dynamic link library (DLL)
!MESSAGE
!MESSAGE <target> may be: !MESSAGE <target> may be:
!MESSAGE - clean - perform a clean for CFG !MESSAGE - clean - perform a clean for CFG
!MESSAGE - experimental - build CFG with experimental !MESSAGE - experimental - build CFG with experimental
!MESSAGE . features enabled. Requires zlib. !MESSAGE . features enabled.
!MESSAGE - (empty) or all - build all targets for CFG
!MESSAGE !MESSAGE
!MESSAGE <rtlibcfg> controls the runtime library linkage - can be 'static' or 'dynamic'. !MESSAGE RTLIBCFG controls the runtime library linkage - 'static' or 'dynamic'.
!MESSAGE <target> can be left blank in which case all is assumed !MESSAGE OBJDIR is the path where you like to build (obj, bins, etc.),
!MESSAGE <path> is the path where you like to build (obj, bins, etc.) !MESSAGE defaults to ..\obj
!MESSAGE default to ..\obj\
!IF "$(CFG)" != "" !IF "$(CFG)" != ""
!MESSAGE !MESSAGE
@ -131,94 +138,114 @@ CFGSET = TRUE
# A config was provided, so the library can be built. # A config was provided, so the library can be built.
# #
X_OBJS= \ DEC_OBJS = \
$(DIROBJ)\dec\frame.obj \ $(DIROBJ)\dec\alpha.obj \
$(DIROBJ)\dec\quant.obj \ $(DIROBJ)\dec\buffer.obj \
$(DIROBJ)\dec\tree.obj \ $(DIROBJ)\dec\frame.obj \
$(DIROBJ)\dec\vp8.obj \ $(DIROBJ)\dec\idec.obj \
$(DIROBJ)\dec\webp.obj \ $(DIROBJ)\dec\io.obj \
$(DIROBJ)\dec\io.obj \ $(DIROBJ)\dec\layer.obj \
$(DIROBJ)\dec\buffer.obj \ $(DIROBJ)\dec\quant.obj \
$(DIROBJ)\dec\idec.obj \ $(DIROBJ)\dec\tree.obj \
$(DIROBJ)\dec\alpha.obj \ $(DIROBJ)\dec\vp8.obj \
$(DIROBJ)\dec\layer.obj \ $(DIROBJ)\dec\vp8l.obj \
$(DIROBJ)\enc\analysis.obj \ $(DIROBJ)\dec\webp.obj \
$(DIROBJ)\enc\config.obj \
$(DIROBJ)\enc\cost.obj \
$(DIROBJ)\enc\frame.obj \
$(DIROBJ)\enc\filter.obj \
$(DIROBJ)\enc\iterator.obj \
$(DIROBJ)\enc\picture.obj \
$(DIROBJ)\enc\quant.obj \
$(DIROBJ)\enc\syntax.obj \
$(DIROBJ)\enc\tree.obj \
$(DIROBJ)\enc\webpenc.obj \
$(DIROBJ)\enc\alpha.obj \
$(DIROBJ)\enc\layer.obj \
$(DIROBJ)\dsp\enc.obj \
$(DIROBJ)\dsp\enc_sse2.obj \
$(DIROBJ)\dsp\upsampling.obj \
$(DIROBJ)\dsp\upsampling_sse2.obj \
$(DIROBJ)\dsp\dec.obj \
$(DIROBJ)\dsp\dec_sse2.obj \
$(DIROBJ)\dsp\cpu.obj \
$(DIROBJ)\dsp\yuv.obj \
$(DIROBJ)\utils\bit_reader.obj \
$(DIROBJ)\utils\bit_writer.obj \
$(DIROBJ)\utils\thread.obj \
$(RESOURCE)
EXAMPLES_OBJS = \ DSP_OBJS = \
$(DIROBJ)\examples\cwebp.obj \ $(DIROBJ)\dsp\cpu.obj \
$(DIROBJ)\examples\dwebp.obj $(DIROBJ)\dsp\dec.obj \
$(DIROBJ)\dsp\dec_sse2.obj \
$(DIROBJ)\dsp\enc.obj \
$(DIROBJ)\dsp\enc_sse2.obj \
$(DIROBJ)\dsp\lossless.obj \
$(DIROBJ)\dsp\upsampling.obj \
$(DIROBJ)\dsp\upsampling_sse2.obj \
$(DIROBJ)\dsp\yuv.obj \
all: $(DIRLIB)\$(TARGET) $(DIRBIN)\dwebp.exe $(DIRBIN)\cwebp.exe EX_UTIL_OBJS = \
$(DIROBJ)\examples\example_util.obj \
ENC_OBJS = \
$(DIROBJ)\enc\alpha.obj \
$(DIROBJ)\enc\analysis.obj \
$(DIROBJ)\enc\backward_references.obj \
$(DIROBJ)\enc\config.obj \
$(DIROBJ)\enc\cost.obj \
$(DIROBJ)\enc\filter.obj \
$(DIROBJ)\enc\frame.obj \
$(DIROBJ)\enc\histogram.obj \
$(DIROBJ)\enc\iterator.obj \
$(DIROBJ)\enc\layer.obj \
$(DIROBJ)\enc\picture.obj \
$(DIROBJ)\enc\quant.obj \
$(DIROBJ)\enc\syntax.obj \
$(DIROBJ)\enc\tree.obj \
$(DIROBJ)\enc\vp8l.obj \
$(DIROBJ)\enc\webpenc.obj \
MUX_OBJS = \
$(DIROBJ)\mux\demux.obj \
$(DIROBJ)\mux\muxedit.obj \
$(DIROBJ)\mux\muxinternal.obj \
$(DIROBJ)\mux\muxread.obj \
UTILS_OBJS = \
$(DIROBJ)\utils\bit_reader.obj \
$(DIROBJ)\utils\bit_writer.obj \
$(DIROBJ)\utils\color_cache.obj \
$(DIROBJ)\utils\filters.obj \
$(DIROBJ)\utils\huffman.obj \
$(DIROBJ)\utils\huffman_encode.obj \
$(DIROBJ)\utils\quant_levels.obj \
$(DIROBJ)\utils\rescaler.obj \
$(DIROBJ)\utils\thread.obj \
$(DIROBJ)\utils\utils.obj \
LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS) $(LIBWEBP_OBJS)
LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS)
OUT_LIBS = $(LIBWEBP)
OUT_EXAMPLES = $(DIRBIN)\cwebp.exe $(DIRBIN)\dwebp.exe
all: $(OUT_LIBS) $(OUT_EXAMPLES)
$(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj
$(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj
$(DIRBIN)\webpmux.exe: $(DIROBJ)\examples\webpmux.obj $(LIBWEBPMUX)
$(DIRBIN)\webpmux.exe: $(EX_UTIL_OBJS) $(LIBWEBP)
$(OUT_EXAMPLES): $(EX_UTIL_OBJS) $(LIBWEBP)
# Additional include and library paths (for zlib) can be passed via the CL and
# LINK environment variables respectively:
# > set CL=/I\zlib\include
# > set LINK=\zlib\zlib.lib
# > nmake /f Makefile.vc CFG=release-static experimental
experimental: experimental:
$(MAKE) /f Makefile.vc \ $(MAKE) /f Makefile.vc \
CFG=$(CFG) CFLAGS="$(CFLAGS) /DWEBP_EXPERIMENTAL_FEATURES" /$(MAKEFLAGS) CFG=$(CFG) \
CFLAGS="$(CFLAGS) /DWEBP_EXPERIMENTAL_FEATURES" /$(MAKEFLAGS)
$(DIRLIB)\$(TARGET): $(X_OBJS) $(LIBWEBP): $(LIBWEBP_OBJS)
$(LNK) $(LFLAGS) $(X_OBJS) $(LIBWEBPMUX): $(LIBWEBPMUX_OBJS)
$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS): $(OUTPUT_DIRS)
!IF "$(DLLBUILD)" == "TRUE"
$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS): $(DIROBJ)\$(DLLINC) $(DIROBJ)\$(DLLC)
{$(DIROBJ)}.c{$(DIROBJ)}.obj:
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$@ $<
$(LIBWEBPMUX): $(LIBWEBP)
$(LIBWEBP) $(LIBWEBPMUX):
$(LNKDLL) /out:$(DIRBIN)\$(@B:_dll=.dll) /implib:$@ $(LFLAGS) $**
-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y -xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
$(X_OBJS): $(DIROBJ)\enc $(DIROBJ)\dec $(DIROBJ)\dsp $(DIROBJ)\utils $(DIRLIB) $(DIRINC) $(DIRBIN)
!IF "$(DLLBUILD)" == "TRUE"
$(X_OBJS): $(DIROBJ)\$(DLLINC)
clean:: clean::
@-erase /s $(DIROBJ)\$(DLLINC) 2> NUL @-erase /s $(DIROBJ)\$(DLLC) $(DIROBJ)\$(DLLINC) 2> NUL
!ELSE
$(LIBWEBP) $(LIBWEBPMUX):
$(LNKLIB) /out:$@ $(LFLAGS) $**
-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
!ENDIF !ENDIF
$(EXAMPLES_OBJS): $(DIROBJ)\examples $(DIRLIB)\$(TARGET) $(OUTPUT_DIRS):
@if not exist "$(@)" mkdir "$(@)"
$(DIROBJ)\enc:
@if not exist "$(DIROBJ)\enc" mkdir $(DIROBJ)\enc
$(DIROBJ)\examples:
@if not exist "$(DIROBJ)\examples" mkdir $(DIROBJ)\examples
$(DIROBJ)\dec:
@if not exist "$(DIROBJ)\dec" mkdir $(DIROBJ)\dec
$(DIROBJ)\dsp:
@if not exist "$(DIROBJ)\dsp" mkdir $(DIROBJ)\dsp
$(DIROBJ)\utils:
@if not exist "$(DIROBJ)\utils" mkdir $(DIROBJ)\utils
$(DIRLIB):
@if not exist "$(DIRLIB)" mkdir $(DIRLIB)
$(DIRINC):
@if not exist "$(DIRINC)" mkdir $(DIRINC)
$(DIRBIN):
@if not exist "$(DIRBIN)" mkdir $(DIRBIN)
# generate a helper include to define WEBP_EXTERN suitable for the DLL build # generate a helper include to define WEBP_EXTERN suitable for the DLL build
$(DIROBJ)\$(DLLINC): $(DIROBJ)\$(DLLINC):
@ -227,20 +254,33 @@ $(DIROBJ)\$(DLLINC):
@echo #define WEBP_EXTERN(type) __declspec(dllexport) type >> $@ @echo #define WEBP_EXTERN(type) __declspec(dllexport) type >> $@
@echo #endif /* WEBP_DLL_H_ */ >> $@ @echo #endif /* WEBP_DLL_H_ */ >> $@
# expose a WebPFree() function for use in managed code
$(DIROBJ)\$(DLLC): $(DIROBJ)\$(DLLINC)
@echo #include ^<stdlib.h^> > $@
@echo #include "webp_dll.h" >> $@
@echo // This function should be used in place of free() for memory >> $@
@echo // returned by the WebP API. >> $@
@echo WEBP_EXTERN(void) WebPFree(void* ptr) { >> $@
@echo free(ptr); >> $@
@echo } >> $@
.SUFFIXES: .c .obj .res .exe .SUFFIXES: .c .obj .res .exe
{examples}.c{$(DIROBJ)\examples}.obj: {examples}.c{$(DIROBJ)\examples}.obj::
$(CC) $(CFLAGS) /Fo"$@" $< $(CC) $(CFLAGS) /Fd$(DIROBJ)\examples\ /Fo$(DIROBJ)\examples\ $<
{src\dec}.c{$(DIROBJ)\dec}.obj: {src\dec}.c{$(DIROBJ)\dec}.obj::
$(CC) $(CFLAGS) /Fo"$@" $< $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dec\ $<
{src\enc}.c{$(DIROBJ)\enc}.obj: {src\dsp}.c{$(DIROBJ)\dsp}.obj::
$(CC) $(CFLAGS) /Fo"$@" $< $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dsp\ $<
{src\dsp}.c{$(DIROBJ)\dsp}.obj: {src\enc}.c{$(DIROBJ)\enc}.obj::
$(CC) $(CFLAGS) /Fo"$@" $< $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\enc\ $<
{src\utils}.c{$(DIROBJ)\utils}.obj: {src\mux}.c{$(DIROBJ)\mux}.obj::
$(CC) $(CFLAGS) /Fo"$@" $< $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\mux\ $<
{src\utils}.c{$(DIROBJ)\utils}.obj::
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\utils\ $<
{$(DIROBJ)\examples}.obj{$(DIRBIN)}.exe: {$(DIROBJ)\examples}.obj{$(DIRBIN)}.exe:
$(LNKEXE) $(LDFLAGS) /OUT:"$@" $< ole32.lib windowscodecs.lib shlwapi.lib $(DIRLIB)\$(TARGET) $(LNKEXE) $(LDFLAGS) /OUT:$@ $** \
ole32.lib windowscodecs.lib shlwapi.lib
$(MT) -manifest $@.manifest -outputresource:$@;1 $(MT) -manifest $@.manifest -outputresource:$@;1
del $@.manifest del $@.manifest

21
NEWS
View File

@ -1,3 +1,24 @@
- 8/3/12: version 0.2.0
* Add support for ARGB -> YUVA conversion for lossless decoder
New functions: WebPINewYUVA, WebPIDecGetYUVA
* Add stats for lossless and alpha encoding
* Security related hardening: allocation and size checks
* Add PAM output support to dwebp
- 7/19/12: version 0.1.99
* This is a pre-release of 0.2.0, not an rc to allow for further
incompatible changes based on user feedback.
* Alpha channel encode/decode support.
* Lossless encoder/decoder.
* Add TIFF input support to cwebp.
Incompatible changes:
* The encode ABI has been modified to support alpha encoding.
* Deprecated function WebPINew() has been removed.
* Decode function signatures have changed to consistently use size_t over
int/uint32_t.
* decode_vp8.h is no longer installed system-wide.
* cwebp will encode the alpha channel if present.
- 9/19/11: version 0.1.3 - 9/19/11: version 0.1.3
* Advanced decoding APIs. * Advanced decoding APIs.
* On-the-fly cropping and rescaling of images. * On-the-fly cropping and rescaling of images.

130
README
View File

@ -4,7 +4,7 @@
\__\__/\____/\_____/__/ ____ ___ \__\__/\____/\_____/__/ ____ ___
/ _/ / \ \ / _ \/ _/ / _/ / \ \ / _ \/ _/
/ \_/ / / \ \ __/ \__ / \_/ / / \ \ __/ \__
\____/____/\_____/_____/____/v0.1.3 \____/____/\_____/_____/____/v0.2.0
Description: Description:
============ ============
@ -13,7 +13,7 @@ WebP codec: library to encode and decode images in WebP format. This package
contains the library that can be used in other programs to add WebP support, contains the library that can be used in other programs to add WebP support,
as well as the command line tools 'cwebp' and 'dwebp'. as well as the command line tools 'cwebp' and 'dwebp'.
See http://code.google.com/speed/webp See http://developers.google.com/speed/webp
Latest sources are available from http://www.webmproject.org/code/ Latest sources are available from http://www.webmproject.org/code/
@ -49,11 +49,13 @@ will build the binaries examples/cwebp and examples/dwebp, along
with the static library src/libwebp.a. No system-wide installation with the static library src/libwebp.a. No system-wide installation
is supplied, as this is a simple alternative to the full installation is supplied, as this is a simple alternative to the full installation
system based on the autoconf tools (see below). system based on the autoconf tools (see below).
Please refer the makefile.unix for additional details and customizations. Please refer to the makefile.unix for additional details and customizations.
Using autoconf tools: Using autoconf tools:
--------------------- ---------------------
./autogen.sh When building from git sources, you will need to run autogen.sh to generate the
configure script.
./configure ./configure
make make
make install make install
@ -61,7 +63,6 @@ make install
should be all you need to have the following files should be all you need to have the following files
/usr/local/include/webp/decode.h /usr/local/include/webp/decode.h
/usr/local/include/webp/decode_vp8.h
/usr/local/include/webp/encode.h /usr/local/include/webp/encode.h
/usr/local/include/webp/types.h /usr/local/include/webp/types.h
/usr/local/lib/libwebp.* /usr/local/lib/libwebp.*
@ -85,17 +86,22 @@ Currently the following functions are mapped:
Decode: Decode:
WebPGetDecoderVersion WebPGetDecoderVersion
WebPGetInfo WebPGetInfo
WebPDecodeRGB
WebPDecodeRGBA WebPDecodeRGBA
WebPDecodeARGB WebPDecodeARGB
WebPDecodeBGR
WebPDecodeBGRA WebPDecodeBGRA
WebPDecodeBGR
WebPDecodeRGB
Encode: Encode:
WebPGetEncoderVersion WebPGetEncoderVersion
WebPEncodeRGB
WebPEncodeRGBA WebPEncodeRGBA
WebPEncodeBGR
WebPEncodeBGRA WebPEncodeBGRA
WebPEncodeRGB
WebPEncodeBGR
WebPEncodeLosslessRGBA
WebPEncodeLosslessBGRA
WebPEncodeLosslessRGB
WebPEncodeLosslessBGR
Java bindings: Java bindings:
@ -111,9 +117,13 @@ decoding (dwebp) images.
The easiest use should look like: The easiest use should look like:
cwebp input.png -q 80 -o output.webp cwebp input.png -q 80 -o output.webp
which will convert the input PNG or JPEG file to a WebP file using a which will convert the input file to a WebP file using a quality factor of 80
quality factor of 80 on a 0->100 scale (0 being the lowest quality, on a 0->100 scale (0 being the lowest quality, 100 being the best. Default
100 being the best. Default value is 75). value is 75).
You might want to try the -lossless flag too, which will compress the source
(in RGBA format) without any loss. The -q quality parameter will in this case
control the amount of processing time spent trying to make the output file as
small as possible.
A longer list of options is available using the -longhelp command line flag: A longer list of options is available using the -longhelp command line flag:
@ -121,18 +131,21 @@ A longer list of options is available using the -longhelp command line flag:
Usage: Usage:
cwebp [-preset <...>] [options] in_file [-o out_file] cwebp [-preset <...>] [options] in_file [-o out_file]
If input size (-s) for an image is not specified, it is assumed to be a either If input size (-s) for an image is not specified, it is assumed to be a PNG,
PNG or JPEG format file. JPEG or TIFF file.
options: options:
-h / -help ............ short help -h / -help ............ short help
-H / -longhelp ........ long help -H / -longhelp ........ long help
-q <float> ............. quality factor (0:small..100:big) -q <float> ............. quality factor (0:small..100:big)
-alpha_q <int> ......... Transparency-compression quality (0..100).
-preset <string> ....... Preset setting, one of: -preset <string> ....... Preset setting, one of:
default, photo, picture, default, photo, picture,
drawing, icon, text drawing, icon, text
-preset must come first, as it overwrites other parameters. -preset must come first, as it overwrites other parameters.
-m <int> ............... compression method (0=fast, 6=slowest) -m <int> ............... compression method (0=fast, 6=slowest)
-segments <int> ........ number of segments to use (1..4) -segments <int> ........ number of segments to use (1..4)
-size <int> ............ Target size (in bytes)
-psnr <float> .......... Target PSNR (in dB. typically: 42)
-s <int> <int> ......... Input size (width x height) for YUV -s <int> <int> ......... Input size (width x height) for YUV
-sns <int> ............. Spatial Noise Shaping (0:off, 100:max) -sns <int> ............. Spatial Noise Shaping (0:off, 100:max)
@ -141,25 +154,31 @@ options:
-strong ................ use strong filter instead of simple. -strong ................ use strong filter instead of simple.
-partition_limit <int> . limit quality to fit the 512k limit on -partition_limit <int> . limit quality to fit the 512k limit on
the first partition (0=no degradation ... 100=full) the first partition (0=no degradation ... 100=full)
-alpha_comp <int> ...... set the transparency-compression
-noalpha ............... discard any transparency information.
-pass <int> ............ analysis pass number (1..10) -pass <int> ............ analysis pass number (1..10)
-partitions <int> ...... number of partitions to use (0..3)
-crop <x> <y> <w> <h> .. crop picture with the given rectangle -crop <x> <y> <w> <h> .. crop picture with the given rectangle
-resize <w> <h> ........ resize picture (after any cropping) -resize <w> <h> ........ resize picture (after any cropping)
-map <int> ............. print map of extra info. -map <int> ............. print map of extra info.
-print_ssim ............ prints averaged SSIM distortion.
-print_psnr ............ prints averaged PSNR distortion.
-d <file.pgm> .......... dump the compressed output (PGM file). -d <file.pgm> .......... dump the compressed output (PGM file).
-alpha_method <int> .... Transparency-compression method (0..1)
-alpha_filter <string> . predictive filtering for alpha plane.
One of: none, fast (default) or best.
-alpha_cleanup ......... Clean RGB values in transparent area.
-noalpha ............... discard any transparency information.
-lossless .............. Encode image losslessly.
-hint <string> ......... Specify image characteristics hint.
One of: photo, picture or graph
-short ................. condense printed message -short ................. condense printed message
-quiet ................. don't print anything. -quiet ................. don't print anything.
-version ............... print version number and exit. -version ............... print version number and exit.
-noasm ................. disable all assembly optimizations. -noasm ................. disable all assembly optimizations.
-v ..................... verbose, e.g. print encoding/decoding times -v ..................... verbose, e.g. print encoding/decoding times
-progress .............. report encoding progress
Experimental Options: Experimental Options:
-size <int> ............ Target size (in bytes) -af .................... auto-adjust filter strength.
-psnr <float> .......... Target PSNR (in dB. typically: 42)
-af <int> .............. adjust filter strength (0=off, 1=on)
-pre <int> ............. pre-processing filter -pre <int> ............. pre-processing filter
@ -183,10 +202,11 @@ Namely:
but with better quality. but with better quality.
Typical value is around '75'. Typical value is around '75'.
* 'f' option directly links to the filtering strength used by the codec's * 'f' option directly links to the filtering strength used by the codec's
in-loop processing. The higher, the smoother will highly-compressed area in-loop processing. The higher the value, the smoother the
look. This is particularly useful when aiming at very small files. highly-compressed area will look. This is particularly useful when aiming
Typical values are around 20-30. Note that using the option -strong will at very small files. Typical values are around 20-30. Note that using the
change the type of filtering. Use "-f 0" to turn filtering off. option -strong will change the type of filtering. Use "-f 0" to turn
filtering off.
* 'm' controls the trade-off between encoding speed and quality. Default is 4. * 'm' controls the trade-off between encoding speed and quality. Default is 4.
You can try -m 5 or -m 6 to explore more (time-consuming) encoding You can try -m 5 or -m 6 to explore more (time-consuming) encoding
possibilities. A lower value will result in faster encoding at the expense possibilities. A lower value will result in faster encoding at the expense
@ -211,7 +231,8 @@ Usage: dwebp in_file [options] [-o out_file]
Decodes the WebP image file to PNG format [Default] Decodes the WebP image file to PNG format [Default]
Use following options to convert into alternate image formats: Use following options to convert into alternate image formats:
-ppm ......... save the raw RGB samples as color PPM -pam ......... save the raw RGBA samples as a color PAM
-ppm ......... save the raw RGB samples as a color PPM
-pgm ......... save the raw YUV samples as a grayscale PGM -pgm ......... save the raw YUV samples as a grayscale PGM
file with IMC4 layout. file with IMC4 layout.
Other options are: Other options are:
@ -221,10 +242,23 @@ Use following options to convert into alternate image formats:
-mt .......... use multi-threading -mt .......... use multi-threading
-crop <x> <y> <w> <h> ... crop output with the given rectangle -crop <x> <y> <w> <h> ... crop output with the given rectangle
-scale <w> <h> .......... scale the output (*after* any cropping) -scale <w> <h> .......... scale the output (*after* any cropping)
-alpha ....... only save the alpha plane.
-h ....... this help message. -h ....... this help message.
-v ....... verbose (e.g. print encoding/decoding times) -v ....... verbose (e.g. print encoding/decoding times)
-noasm ....... disable all assembly optimizations. -noasm ....... disable all assembly optimizations.
Visualization tool:
===================
There's a little self-serve visualization tool called 'vwebp' under the
examples/ directory. It uses OpenGL to open a simple drawing window and show
a decoded WebP file. It's not yet integrated in the automake or makefile.unix
build system, but you can try to manually compile it using the recommendations
at the top of the source file.
Usage: 'vwebp my_picture.webp'
Encoding API: Encoding API:
============= =============
@ -242,6 +276,20 @@ size_t WebPEncodeBGRA(const uint8_t* bgra, int width, int height, int stride,
They will convert raw RGB samples to a WebP data. The only control supplied They will convert raw RGB samples to a WebP data. The only control supplied
is the quality factor. is the quality factor.
There are some variants for using the lossless format:
size_t WebPEncodeLosslessRGB(const uint8_t* rgb, int width, int height,
int stride, uint8_t** output);
size_t WebPEncodeLosslessBGR(const uint8_t* bgr, int width, int height,
int stride, uint8_t** output);
size_t WebPEncodeLosslessRGBA(const uint8_t* rgba, int width, int height,
int stride, uint8_t** output);
size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, int width, int height,
int stride, uint8_t** output);
Of course in this case, no quality factor is needed since the compression
occurs without loss of the input values, at the expense of larger output sizes.
Advanced encoding API: Advanced encoding API:
---------------------- ----------------------
@ -306,21 +354,19 @@ Decoding API:
This is mainly just one function to call: This is mainly just one function to call:
#include "webp/decode.h" #include "webp/decode.h"
uint8_t* WebPDecodeRGB(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
int *width, int *height); int* width, int* height);
Please have a look at the file src/webp/decode.h for the details. Please have a look at the file src/webp/decode.h for the details.
There are variants for decoding in BGR/RGBA/ARGB/BGRA order, along with There are variants for decoding in BGR/RGBA/ARGB/BGRA order, along with
decoding to raw Y'CbCr samples. One can also decode the image directly into a decoding to raw Y'CbCr samples. One can also decode the image directly into a
pre-allocated buffer. pre-allocated buffer.
To detect a WebP file and gather picture's dimensions, the function: To detect a WebP file and gather the picture's dimensions, the function:
int WebPGetInfo(const uint8_t* data, uint32_t data_size, int WebPGetInfo(const uint8_t* data, size_t data_size,
int *width, int *height); int* width, int* height);
is supplied. No decoding is involved when using it. is supplied. No decoding is involved when using it.
A lower-level API is available from the header file <webp/decode_vp8.h>
Incremental decoding API: Incremental decoding API:
========================= =========================
@ -330,7 +376,11 @@ is stored into an instance of the WebPIDecoder object. This object can be
created with the purpose of decoding either RGB or Y'CbCr samples. created with the purpose of decoding either RGB or Y'CbCr samples.
For instance: For instance:
WebPIDecoder* idec = WebPINew(MODE_BGR); WebPDecBuffer buffer;
WebPInitDecBuffer(&buffer);
buffer.colorspace = MODE_BGR;
...
WebPIDecoder* idec = WebPINewDecoder(&buffer);
As data is made progressively available, this incremental-decoder object As data is made progressively available, this incremental-decoder object
can be used to decode the picture further. There are two (mutually exclusive) can be used to decode the picture further. There are two (mutually exclusive)
@ -344,23 +394,23 @@ or by just mentioning the new size of the transmitted data:
WebPIUpdate(idec, buffer, size_of_transmitted_buffer); WebPIUpdate(idec, buffer, size_of_transmitted_buffer);
Note that 'buffer' can be modified between each calls to WebPIUpdate, in Note that 'buffer' can be modified between each call to WebPIUpdate, in
particular when the buffer is resized to accommodate larger data. particular when the buffer is resized to accommodate larger data.
These functions will return the decoding status: either VP8_STATUS_SUSPENDED if These functions will return the decoding status: either VP8_STATUS_SUSPENDED if
decoding is not finished yet, or VP8_STATUS_OK when decoding is done. decoding is not finished yet or VP8_STATUS_OK when decoding is done. Any other
Any other status is an error condition. status is an error condition.
The idec object must always be released (even upon an error condition) The 'idec' object must always be released (even upon an error condition) by
by calling: WebPDelete(idec). calling: WebPDelete(idec).
To retrieve partially decoded picture samples, one must use the corresponding To retrieve partially decoded picture samples, one must use the corresponding
method: WebPIDecGetRGB or WebPIDecGetYUV. method: WebPIDecGetRGB or WebPIDecGetYUVA.
It will return the last displayable pixel row. It will return the last displayable pixel row.
Lastly, note that decoding can also be performed into a pre-allocated pixel Lastly, note that decoding can also be performed into a pre-allocated pixel
buffer. This buffer must be passed when creating a WebPIDecoder, calling buffer. This buffer must be passed when creating a WebPIDecoder, calling
WebPINewRGB() or WebPINewYUV(). WebPINewRGB() or WebPINewYUVA().
Please have a look at the src/webp/decode.h header for further details. Please have a look at the src/webp/decode.h header for further details.

117
README.mux Normal file
View File

@ -0,0 +1,117 @@
 __ __ ____ ____ ____ __ __ _ __ __
/ \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\
\ / __/ _ \ __/ / / (_/ /__
\__\__/\_____/_____/__/ \__//_/\_____/__/___/
Description:
============
WebP Mux: library to create a WebP container object for features like
color profile, XMP metadata, animation & tiling. A reference command line
tool 'webpmux' and WebP container specification 'doc/webp-container-spec.txt'
are also provided in this package.
WebP Mux tool:
==============
The examples/ directory contains a tool (webpmux) for manipulating WebP
files. The webpmux tool can be used to create a WebP container file and to
extract or strip relevant data from the container file.
A list of options is available using the -help command line flag:
> webpmux -help
Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
webpmux -set SET_OPTIONS INPUT -o OUTPUT
webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
webpmux -tile TILE_OPTIONS [-tile...] -o OUTPUT
webpmux -frame FRAME_OPTIONS [-frame...] -loop LOOP_COUNT -o OUTPUT
webpmux -info INPUT
webpmux [-h|-help]
GET_OPTIONS:
Extract relevant data.
icc Get ICCP Color profile.
xmp Get XMP metadata.
tile n Get nth tile.
frame n Get nth frame.
SET_OPTIONS:
Set color profile/metadata.
icc Set ICC Color profile.
xmp Set XMP metadata.
STRIP_OPTIONS:
Strip color profile/metadata.
icc Strip ICCP color profile.
xmp Strip XMP metadata.
TILE_OPTIONS(i):
Create tiled image.
file_i +xi+yi
where: 'file_i' is the i'th tile (webp format),
'xi','yi' specify the image offset for this tile.
FRAME_OPTIONS(i):
Create animation.
file_i +xi+yi+di
where: 'file_i' is the i'th animation frame (webp format),
'xi','yi' specify the image offset for this frame.
'di' is the pause duration before next frame.
INPUT & OUTPUT are in webp format.
WebP Mux API:
==============
The WebP Mux API contains methods for adding data to and reading data from
WebPMux (a WebP container object). This API currently supports XMP metadata,
color profile, animation & tiling. Other features will be added in subsequent
releases.
Example#1 (pseudo code): Creating a WebPMux object with image data, color
profile & XMP metadata.
int copy_data = 0;
WebPMux* mux = WebPMuxNew();
// ... (Prepare image data).
WebPMuxSetImage(mux, &image, copy_data);
// ... (Prepare ICCP color profile data).
WebPMuxSetColorProfile(mux, &icc_profile, copy_data);
// ... (Prepare XMP metadata).
WebPMuxSetMetadata(mux, &xmp, copy_data);
// Get data from mux in WebP RIFF format.
WebPMuxAssemble(mux, &output_data);
WebPMuxDelete(mux);
// ... (Consume output_data; e.g. write output_data.bytes_ to file).
WebPDataClear(&output_data);
Example#2 (pseudo code): Get image & color profile data from a WebP file.
int copy_data = 0;
// ... (Read data from file).
WebPMux* mux = WebPMuxCreate(&data, copy_data);
WebPMuxGetImage(mux, &image);
// ... (Consume image; e.g. call WebPDecode() to decode the data).
WebPMuxGetColorProfile(mux, &icc_profile);
// ... (Consume icc_profile).
WebPMuxDelete(mux);
free(data);
For detailed Mux API reference, please refer to the header file (src/webp/mux.h)
Bugs:
=====
Please report all bugs to our issue tracker:
http://code.google.com/p/webp/issues
Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting-patches/
Discuss:
========
Email: webp-discuss@webmproject.org
Web: http://groups.google.com/a/webmproject.org/group/webp-discuss

View File

@ -1,44 +1,67 @@
AC_INIT([libwebp], [0.1.3], AC_INIT([libwebp], [0.2.0],
[http://code.google.com/p/webp/issues],, [http://code.google.com/p/webp/issues],,
[http://code.google.com/speed/webp]) [http://developers.google.com/speed/webp])
AC_CANONICAL_TARGET AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
AC_PROG_LIBTOOL AC_PROG_LIBTOOL
AM_PROG_CC_C_O AM_PROG_CC_C_O
AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH], dnl === Enable less verbose output when building.
[Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]), dnl === If an older aclocal exits with an error comment these lines out.
[pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) m4_define_default([AM_SILENT_RULES], [])
AM_SILENT_RULES
AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=DIR],
[Path to the pkgconfig directory @<:@LIBDIR/pkgconfig@:>@]),
[pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
AC_SUBST([pkgconfigdir]) AC_SUBST([pkgconfigdir])
dnl === Check libz is present dnl === WITHLIB_OPTION([opt_pfx], [outvar_pfx])
dnl === Defines --with-<opt_pfx>{include,lib}dir options which set
dnl === the variables <outvar_pfx>_{INCLUDES,LIBS}.
AC_DEFUN([WITHLIB_OPTION],
[AC_ARG_WITH([$1includedir],
AS_HELP_STRING([--with-$1includedir=DIR],
[use $2 includes from DIR]),
$2_INCLUDES="-I$withval")
AC_ARG_WITH([$1libdir],
AS_HELP_STRING([--with-$1libdir=DIR],
[use $2 libraries from DIR]),
[$2_LIBS="-L$withval"])])
if test "$enable_experimental" = "yes"; then dnl === Check for pthread support
AC_CHECK_HEADER(zlib.h, AC_ARG_ENABLE([threading],
AC_CHECK_LIB(z, gzsetparams,,AC_MSG_ERROR(zlib library not found)), AS_HELP_STRING([--disable-threading],
AC_MSG_ERROR(zlib not available - no zlib.h) [Disable detection of thread support]),,
) [enable_threading=yes])
if test "$enable_threading" = "yes"; then
AC_MSG_NOTICE([checking for threading support...])
AX_PTHREAD([AC_DEFINE([WEBP_USE_THREAD], [1],
[Undefine this to disable thread support.])
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC"
],
[enable_threading=no])
fi fi
AC_MSG_NOTICE([checking if threading is enabled... ${enable_threading-no}])
dnl === check for PNG support === dnl === check for PNG support ===
PNG_INCLUDES="" PNG_INCLUDES=""
PNG_LIBS="" PNG_LIBS=""
AC_PATH_PROG(LIBPNG_CONFIG, libpng-config) AC_PATH_PROGS(LIBPNG_CONFIG,
[libpng-config libpng15-config libpng14-config libpng12-config])
if test -n "$LIBPNG_CONFIG"; then if test -n "$LIBPNG_CONFIG"; then
PNG_INCLUDES=`$LIBPNG_CONFIG --cflags` PNG_INCLUDES=`$LIBPNG_CONFIG --cflags`
PNG_PREFIX=`$LIBPNG_CONFIG --prefix` PNG_PREFIX=`$LIBPNG_CONFIG --prefix`
if test "${PNG_PREFIX}/lib" != "/usr/lib" ; then if test "${PNG_PREFIX}/lib" != "/usr/lib" ; then
PNG_LIBS="-L${PNG_PREFIX}/lib" PNG_LIBS="-L${PNG_PREFIX}/lib"
fi fi
PNG_LIBS="$PNG_LIBS `$LIBPNG_CONFIG --libs`"
fi fi
AC_ARG_WITH(pngincludedir, WITHLIB_OPTION([png], [PNG])
[--with-pngincludedir=DIR use PNG includes from DIR],
PNG_INCLUDES="-I$withval")
AC_ARG_WITH(pnglibdir,
[--with-pnglibdir=DIR use PNG libraries from DIR],
[PNG_LIBS="-L$withval"])
SAVED_CPPFLAGS=$CPPFLAGS SAVED_CPPFLAGS=$CPPFLAGS
SAVED_LIBS=$LIBS SAVED_LIBS=$LIBS
@ -46,14 +69,22 @@ CPPFLAGS="$PNG_INCLUDES $CPPFLAGS"
LIBS="$PNG_LIBS $LIBS" LIBS="$PNG_LIBS $LIBS"
AC_CHECK_HEADER(png.h, AC_CHECK_HEADER(png.h,
AC_CHECK_LIB(png, main, AC_SEARCH_LIBS(png_get_libpng_ver, [png],
[PNG_LIBS="$PNG_LIBS -lpng" [test "$ac_cv_search_png_get_libpng_ver" = "none required" \
PNG_INCLUDES="$PNG_INCLUDES -DWEBP_HAVE_PNG" || PNG_LIBS="$PNG_LIBS $ac_cv_search_png_get_libpng_ver"
AC_DEFINE(WEBP_HAVE_PNG, [1], [Set to 1 if PNG library is installed]) PNG_INCLUDES="$PNG_INCLUDES -DWEBP_HAVE_PNG"
], AC_DEFINE(WEBP_HAVE_PNG, [1],
AC_MSG_WARN(Optional png library not found), [Set to 1 if PNG library is installed])
[$MATH_LIBS]), ],
AC_MSG_WARN(png library not available - no png.h) [AC_MSG_WARN(Optional png library not found)
PNG_LIBS=""
PNG_INCLUDES=""
],
[$MATH_LIBS]),
[AC_MSG_WARN(png library not available - no png.h)
PNG_LIBS=""
PNG_INCLUDES=""
],
) )
AC_SUBST(PNG_LIBS) AC_SUBST(PNG_LIBS)
AC_SUBST(PNG_INCLUDES) AC_SUBST(PNG_INCLUDES)
@ -65,12 +96,7 @@ dnl === check for JPEG support ===
JPEG_INCLUDES="" JPEG_INCLUDES=""
JPEG_LIBS="" JPEG_LIBS=""
AC_ARG_WITH(jpegincludedir, WITHLIB_OPTION([jpeg], [JPEG])
[--with-jpegincludedir=DIR use JPEG includes from DIR],
JPEG_INCLUDES="-I$withval")
AC_ARG_WITH(jpeglibdir,
[--with-jpeglibdir=DIR use JPEG libraries from DIR],
[JPEG_LIBS="-L$withval"])
SAVED_CPPFLAGS=$CPPFLAGS SAVED_CPPFLAGS=$CPPFLAGS
SAVED_LIBS=$LIBS SAVED_LIBS=$LIBS
@ -81,7 +107,8 @@ AC_CHECK_HEADER(jpeglib.h,
AC_CHECK_LIB(jpeg, jpeg_set_defaults, AC_CHECK_LIB(jpeg, jpeg_set_defaults,
[JPEG_LIBS="$JPEG_LIBS -ljpeg" [JPEG_LIBS="$JPEG_LIBS -ljpeg"
JPEG_INCLUDES="$JPEG_INCLUDES -DWEBP_HAVE_JPEG" JPEG_INCLUDES="$JPEG_INCLUDES -DWEBP_HAVE_JPEG"
AC_DEFINE(WEBP_HAVE_JPEG, [1], [Set to 1 if JPEG library is installed]) AC_DEFINE(WEBP_HAVE_JPEG, [1],
[Set to 1 if JPEG library is installed])
], ],
AC_MSG_WARN(Optional jpeg library not found), AC_MSG_WARN(Optional jpeg library not found),
[$MATH_LIBS]), [$MATH_LIBS]),
@ -93,6 +120,34 @@ AC_SUBST(JPEG_INCLUDES)
CPPFLAGS=$SAVED_CPPFLAGS CPPFLAGS=$SAVED_CPPFLAGS
LIBS=$SAVED_LIBS LIBS=$SAVED_LIBS
dnl === check for TIFF support ===
TIFF_INCLUDES=""
TIFF_LIBS=""
WITHLIB_OPTION([tiff], [TIFF])
SAVED_CPPFLAGS=$CPPFLAGS
SAVED_LIBS=$LIBS
CPPFLAGS="$TIFF_INCLUDES $CPPFLAGS"
LIBS="$TIFF_LIBS $LIBS"
AC_CHECK_HEADER(tiffio.h,
AC_CHECK_LIB(tiff, TIFFGetVersion,
[TIFF_LIBS="$TIFF_LIBS -ltiff"
TIFF_INCLUDES="$TIFF_INCLUDES -DWEBP_HAVE_TIFF"
AC_DEFINE(WEBP_HAVE_TIFF, [1],
[Set to 1 if TIFF library is installed])
],
AC_MSG_WARN(Optional tiff library not found),
[$MATH_LIBS]),
AC_MSG_WARN(tiff library not available - no tiffio.h)
)
AC_SUBST(TIFF_LIBS)
AC_SUBST(TIFF_INCLUDES)
CPPFLAGS=$SAVED_CPPFLAGS
LIBS=$SAVED_LIBS
dnl === check for WIC support === dnl === check for WIC support ===
if test "$target_os" = "mingw32"; then if test "$target_os" = "mingw32"; then
@ -136,11 +191,12 @@ if test "$target_os" = "mingw32"; then
fi fi
fi fi
dnl === If --enable-experimental is defined, add the flag WEBP_EXPERIMENTAL_FEATURES dnl === If --enable-experimental is defined, add -DWEBP_EXPERIMENTAL_FEATURES
USE_EXPERIMENTAL_CODE="" USE_EXPERIMENTAL_CODE=""
AC_MSG_CHECKING(if --enable-experimental option is specified) AC_MSG_CHECKING(if --enable-experimental option is specified)
AC_ARG_ENABLE(experimental, [ --enable-experimental Activate experimental features]) AC_ARG_ENABLE([experimental], AS_HELP_STRING([--enable-experimental],
[Activate experimental features]))
if test "$enable_experimental" = "yes"; then if test "$enable_experimental" = "yes"; then
AC_DEFINE(EXPERIMENTAL,,[Enable experimental code]) AC_DEFINE(EXPERIMENTAL,,[Enable experimental code])
USE_EXPERIMENTAL_CODE="-DWEBP_EXPERIMENTAL_FEATURES" USE_EXPERIMENTAL_CODE="-DWEBP_EXPERIMENTAL_FEATURES"
@ -148,6 +204,14 @@ fi
AC_MSG_RESULT(${enable_experimental-no}) AC_MSG_RESULT(${enable_experimental-no})
AC_SUBST(USE_EXPERIMENTAL_CODE) AC_SUBST(USE_EXPERIMENTAL_CODE)
dnl === Check whether libwebpmux should be built
AC_MSG_CHECKING(whether libwebpmux is to be built)
AC_ARG_ENABLE([experimental-libwebpmux],
AS_HELP_STRING([--enable-experimental-libwebpmux],
[Build libwebpmux @<:@default=no@:>@]))
AC_MSG_RESULT(${enable_experimental_libwebpmux-no})
AM_CONDITIONAL([WANT_MUX], [test "$enable_experimental_libwebpmux" = "yes"])
dnl ========================= dnl =========================
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
@ -156,6 +220,7 @@ AC_CONFIG_FILES([Makefile src/Makefile man/Makefile \
examples/Makefile src/dec/Makefile \ examples/Makefile src/dec/Makefile \
src/enc/Makefile src/dsp/Makefile \ src/enc/Makefile src/dsp/Makefile \
src/utils/Makefile \ src/utils/Makefile \
src/mux/Makefile \
src/libwebp.pc]) src/libwebp.pc])

29
doc/README Normal file
View File

@ -0,0 +1,29 @@
Generate libwebp Container Spec Docs from Text Source
=====================================================
HTML generation requires kramdown [1], easily installed as a
rubygem [2]. Rubygems installation should satisfy dependencies
automatically.
[1]: http://kramdown.rubyforge.org/
[2]: http://rubygems.org/
HTML generation can then be done from the project root:
$ kramdown doc/webp-container-spec.txt --template doc/template.html > \
doc/output/webp-container-spec.html
kramdown can optionally syntax highlight code blocks, using CodeRay [3],
a dependency of kramdown that rubygems will install automatically. The
following will apply inline CSS styling; an external stylesheet is not
needed.
$ kramdown doc/webp-lossless-bitstream-spec.txt --template \
doc/template.html --coderay-css style --coderay-line-numbers ' ' \
--coderay-default-lang c > \
doc/output/webp-lossless-bitstream-spec.html
Optimally, use kramdown 0.13.7 or newer if syntax highlighting desired.
[3]: http://coderay.rubychan.de/

13
doc/TODO Normal file
View File

@ -0,0 +1,13 @@
<louquillio@google.com>, 20111004
* Determine that normative RFC 2119 terms (MUST, SHOULD, MAY, etc.) are
truly intended in all cases where capitalized.
* Several passages could be made clearer.
* Overall edit for scope. Portions are phrased as an introduction to
the 0.1.3 RIFF container additions, rather than a holistic guide to
WebP.
* To wit, suggest s/[spec|specification]/guide/g . "Spec" can imply a
standards track; in any case it's too formal for a work in progress.

94
doc/template.html Normal file
View File

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebP Container Specification</title>
<meta name="generator" content="kramdown <%= ::Kramdown::VERSION %>" />
<style type="text/css">
body {
color: #000;
background-color: #fff;
margin: 10%;
font-family: "Liberation Sans", "DejaVu Sans", "Bitstream Vera Sans", Arial, sans-serif;
line-height: 1.4;
}
h2 {
border-bottom: 1px solid #ccc;
padding-bottom: 0;
}
table {
border-collapse: collapse;
}
th, td {
border: 1px solid #999;
padding: .5em .7em;;
}
th {
color: #fff;
background-color: #000;
}
td {
}
hr {
}
code {
color: #000;
background-color: #f7f7f7;
padding: 0 3px;
font-family: "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", Consolata, monospace;
}
pre {
background-color: #f7f7f7;
padding: 1em;
border: 1px solid #ccc;
width: 42em;
overflow: auto;
font-family: "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", Consolata, monospace;
}
pre code {
background-color: #f7f7f7;
padding: 0; /* Only want padding on inline code, not blocks */
}
pre.terminal {
color: #fff;
background-color: #000;
border: 1px solid #ccc;
max-height: 30em;
}
pre.terminal code {
color: #fff;
background-color: #000;
font-size: smaller;
}
#markdown-toc ul {
list-style-type: disc;
}
ul#markdown-toc {
margin-top: -1em;
visibility: hidden;
-webkit-padding-start: 0;
}
ul#markdown-toc ul {
visibility: visible;
}
ul#markdown-toc ul ul{
visibility: visible;
}
ul#markdown-toc + hr {
margin-bottom: 4em;
}
ol ol { /* Format nested ordered lists */
list-style-type: lower-alpha;
}
dt {
font-style: italic;
font-weight: bold;
}
.caption {
}
</style>
</head>
<body>
<%= @body %>
</body>
</html>

467
doc/webp-container-spec.txt Normal file
View File

@ -0,0 +1,467 @@
<!--
Although you may be viewing an alternate representation, this document
is sourced in Markdown, a light-duty markup scheme, and is optimized for
the [kramdown](http://kramdown.rubyforge.org/) transformer.
See the accompanying README. External link targets are referenced at the
end of this file.
-->
WebP Container Specification
============================
_Working Draft, v0.5, 20120713_
* TOC placeholder
{:toc}
Introduction
------------
WebP is an image format that uses either (i) the VP8 key frame encoding
to compress image data in a lossy way, or (ii) the WebP lossless encoding
(and possibly other encodings in the future). These encoding schemes should
make it more efficient than currently used formats. It is optimized for fast
image transfer over the network (e.g., for websites). This document describes
the structure of a WebP file.
The WebP container (i.e., RIFF container for WebP) allows feature support over
and above the basic use case of WebP (i.e., a file containing a single image
encoded as a VP8 key frame). The WebP container provides additional support
for:
* **Lossless compression.** An image can be losslessly compressed, using the
WebP Lossless Format.
* **Transparency.** An image may have transparency, i.e., an alpha channel.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in [RFC 2119][].
Terminology &amp; Basics
------------------------
A WebP file contains a still image (i.e., an encoded matrix of pixels) and,
optionally, transparency information. In case we need to refer only to the
matrix of pixels, we will call it the _canvas_ of the image.
Below are additional terms used throughout this document:
Code that reads WebP files is referred to as a _reader_, while
code that writes them is referred to as a _writer_.
_uint16_
: A 16-bit, little-endian, unsigned integer.
_uint24_
: A 24-bit, little-endian, unsigned integer.
_uint32_
: A 32-bit, little-endian, unsigned integer.
_1-based_
: An unsigned integer field storing values offset by `-1`. e.g., Such a field
would store value _25_ as _24_.
The basic element of a RIFF file is a _chunk_. It consists of:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Chunk FourCC |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Chunk Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Chunk Payload |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Chunk FourCC: 32 bits
: ASCII four character code or _chunk tag_ used for chunk identification.
Chunk Size: 32 bits (_uint32_)
: The size of the chunk (_ckSize_) not including this field, the chunk
identifier and padding.
Chunk Payload: _Chunk Size_ bytes
: The data payload. If _Chunk Size_ is odd a single padding byte that
SHOULD be `0` is added.
_ChunkHeader('ABCD')_
: This is used to describe the fourcc and size header of individual
chunks, where 'ABCD' is the fourcc for the chunk. This element's
size is 8 bytes.
: Note that, in this specification, all chunk tag characters are in
file order, not in byte order of a uint32 of any particular
architecture.
_list of chunks_
: A concatenation of multiple chunks.
: We will refer to the first chunk as having _position_ 0, the second
as position 1, etc. By _chunk with index 0 among "ABCD"_ we mean
the first chunk among the chunks of type "ABCD" in the list, the
_chunk with index 1 among "ABCD"_ is the second such chunk, etc.
A WebP file MUST begin with a single chunk with a tag 'RIFF'. All
other defined chunks are contained within this chunk. The file SHOULD
NOT contain anything after it.
The maximum size of RIFF's _ckSize_ is 2^32 minus 10 bytes. The size
of the whole file is at most 4GiB minus 2 bytes.
**Note:** some RIFF libraries are said to have bugs when handling files
larger than 1GiB or 2GiB. If you are using an existing library, check
that it handles large files correctly.
The first four bytes of the RIFF chunk contents (i.e., bytes 8-11 of the file)
MUST be the ASCII string "WEBP". They are followed by a list of chunks. As the
size of any chunk is even, the size of the RIFF chunk is also even. The
contents of the chunks in that list will be described in the following sections.
**Note:** RIFF has a convention that all-uppercase chunks are standard
chunks that apply to any RIFF file format, while chunks specific to a
file format are all lowercase. WebP does not follow this convention.
WebP file header
----------------
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 'R' | 'I' | 'F' | 'F' |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| File Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 'W' | 'E' | 'B' | 'P' |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'RIFF': 32 bits
: The ASCII characters 'R' 'I' 'F' 'F'.
File Size: 32 bits (_uint32_)
: The size of the file in bytes starting at offset 8.
'WEBP': 32 bits
: The ASCII characters 'W' 'E' 'B' 'P'.
Simple file format (lossy)
--------------------------
This layout SHOULD be used if the image requires _lossy_ encoding and does not
require transparency or other advanced features provided by the extended format.
Files with this layout are smaller and supported by older software.
Simple WebP (lossy) file format:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| WebP file header (12 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| VP8 chunk |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
VP8 chunk:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8 ') |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| VP8 data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
VP8 data: _Chunk Size_ bytes
: VP8 bitstream data.
The VP8 bitstream format specification can be found at [VP8 Data Format and
Decoding Guide][vp8spec]. Note that the VP8 frame header contains the VP8 frame
width and height. That is assumed to be the width and height of the canvas.
The VP8 specification describes how to decode the image into Y'CbCr
format. To convert to RGB, Rec. 601 SHOULD be used.
Simple file format (lossless)
-----------------------------
**Note:** Older readers may not support files using the lossless format.
This layout SHOULD be used if the image requires _lossless_ encoding (with an
optional transparency channel) and does not require advanced features provided
by the extended format.
Simple WebP (lossless) file format:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| WebP file header (12 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| VP8L chunk |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
VP8L chunk:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8L') |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| VP8L data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
VP8L data: _Chunk Size_ bytes
: VP8L bitstream data.
The current specification of the VP8L bitstream can be found at
[WebP Lossless Bitstream Format][webpllspec]. Note that the VP8L header
contains the VP8L image width and height. That is assumed to be the width
and height of the canvas.
Extended file format
--------------------
**Note:** Older readers may not support files using the extended format.
An extended format file consists of:
* A 'VP8X' chunk with information about features used in the file.
* An optional 'ALPH' chunk with transparency information.
* The image bitstream contained in either a 'VP8 ' or 'VP8L' chunk.
All chunks SHOULD be placed in the same order as listed above. If a chunk
appears in the wrong place, the file is invalid, but readers MAY parse the
file, ignoring the chunks that come too late.
**Rationale:** Setting the order of chunks should allow quicker file
parsing. For example, if an 'ALPH' chunk does not appear in its required
position, a decoder can choose to stop searching for it. The rule of
ignoring late chunks should make programs that need to do a full search
give the same results as the ones stopping early.
Extended WebP file header:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| WebP file header (12 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8X') |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Rsv |L| Rsv | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Canvas Width Minus One | ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
... Canvas Height Minus One |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Reserved (Rsv): 4 bits
: SHOULD be `0`.
Alpha (L): 1 bit
: Set if the file contains some (or all) images with transparency information
("alpha").
Reserved (Rsv): 3 bits
: SHOULD be `0`.
Reserved: 24 bits
: SHOULD be `0`.
Canvas Width Minus One: 24 bits
: _1-based_ width of the canvas in pixels.
The actual canvas width is '1 + Canvas Width Minus One'
Canvas Height Minus One: 24 bits
: _1-based_ height of the canvas in pixels.
The actual canvas height is '1 + Canvas Height Minus One'
The product of _Canvas Width_ and _Canvas Height_ MUST be at most `2^32 - 1`.
Future specifications MAY add more fields.
### Chunks
#### Alpha
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('ALPH') |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Rsv| P | F | C | Alpha Bitstream... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Compression method (C): 2 bits
: The compression method used:
* `0`: No compression.
* `1`: Compressed using the WebP lossless format.
Filtering method (F): 2 bits
: The filtering method used:
* `0`: None.
* `1`: Horizontal filter.
* `2`: Vertical filter.
* `3`: Gradient filter.
For each pixel, filtering is performed using the following calculations.
Assume the alpha values surrounding the current `X` position are labeled as:
C | B |
---+---+
A | X |
We seek to compute the alpha value at position `X`. First, a prediction is
made depending on the filtering method:
* Method `0`: predictor = 0
* Method `1`: predictor = A
* Method `2`: predictor = B
* Method `3`: predictor = clip(A + B - C)
where `clip(v)` is equal to:
* 0 if v < 0
* 255 if v > 255
* v otherwise
The final value is derived by adding the decompressed value `X` to the
predictor and using modulo-256 arithmetic to wrap the [256-511] range
into the [0-255] one:
`alpha = (predictor + X) % 256`
There are special cases for left-most and top-most pixel positions:
* Top-left value at location (0,0) uses 0 as predictor value. Otherwise,
* For horizontal or gradient filtering methods, the left-most pixels at
location (0, y) are predicted using the location (0, y-1) just above.
* For vertical or gradient filtering methods, the top-most pixels at
location (x, 0) are predicted using the location (x-1, 0) on the left.
Pre-processing (P): 2 bits
: These INFORMATIVE bits are used to signal the pre-processing that has
been performed during compression. The decoder can use this information to
e.g. dither the values or smooth the gradients prior to display.
* `0`: no pre-processing
* `1`: level reduction
Decoders are not required to use this information in any specified way.
Reserved (Rsv): 2 bits
: SHOULD be `0`.
Alpha bitstream: _Chunk Size_ - `1` bytes
: Encoded alpha bitstream.
This optional chunk contains encoded alpha data for the image. An image
containing a 'VP8L' chunk SHOULD NOT contain this chunk.
**Rationale**: The transparency information of the image is already part
of the 'VP8L' chunk.
The alpha channel data is stored as uncompressed raw data (when
compression method is '0') or compressed using the lossless format
(when the compression method is '1').
* Raw data: consists of a byte sequence of length width * height,
containing all the 8-bit transparency values in scan order.
* Lossless format compression: the byte sequence is a compressed
image-stream (as described in the [WebP Lossless Bitstream Format]
[webpllspec]) of implicit dimension width x height. That is, this
image-stream does NOT contain any headers describing the image dimension.
**Rationale**: the dimension is already known from other sources,
so storing it again would be redundant and error-prone.
Once the image-stream is decoded into ARGB color values, following
the process described in the lossless format specification, the
transparency information must be extracted from the *green* channel
of the ARGB quadruplet.
**Rationale**: the green channel is allowed extra transformation
steps in the specification -- unlike the other channels -- that can
improve compression.
#### Bitstream (VP8/VP8L)
This chunk contains compressed image data.
A bitstream chunk may be either (i) a VP8 chunk, using "VP8 " (note the
significant fourth-character space) as its tag _or_ (ii) a VP8L chunk, using
"VP8L" as its tag.
The formats of VP8 and VP8L chunks are as described in sections
[Simple file format (lossy)](#simple-file-format-lossy)
and [Simple file format (lossless)](#simple-file-format-lossless) respectively.
#### Unknown Chunks
A file MAY contain other unknown chunks. Readers SHOULD ignore these chunks.
Writers SHOULD preserve them in their original order.
Example file layouts
--------------------
A lossy encoded image with alpha may look as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RIFF/WEBP
+- VP8X (descriptions of features used)
+- ALPH (alpha bitstream)
+- VP8 (bitstream)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A losslessly encoded image may look as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RIFF/WEBP
+- VP8X (descriptions of features used)
+- XYZW (unknown chunk)
+- VP8L (lossless bitstream)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[vp8spec]: http://tools.ietf.org/html/rfc6386
[webpllspec]: https://gerrit.chromium.org/gerrit/gitweb?p=webm/libwebp.git;a=blob;f=doc/webp-lossless-bitstream-spec.txt;hb=master
[metadata]: http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf
[rfc 2119]: http://tools.ietf.org/html/rfc2119

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,25 @@
AM_CPPFLAGS = -I$(top_srcdir)/src AM_CPPFLAGS = -I$(top_srcdir)/src
bin_PROGRAMS = dwebp cwebp bin_PROGRAMS = dwebp cwebp
if WANT_MUX
bin_PROGRAMS += webpmux
endif
noinst_LTLIBRARIES = libexampleutil.la
libexampleutil_la_SOURCES = example_util.c
libexampleutilinclude_HEADERS = example_util.h
libexampleutilincludedir =
dwebp_SOURCES = dwebp.c stopwatch.h dwebp_SOURCES = dwebp.c stopwatch.h
dwebp_CPPFLAGS = $(AM_CPPFLAGS) $(PNG_INCLUDES) $(JPEG_INCLUDES) $(USE_EXPERIMENTAL_CODE) dwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
dwebp_LDADD = ../src/libwebp.la $(PNG_LIBS) $(JPEG_LIBS) dwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES)
dwebp_LDADD = libexampleutil.la ../src/libwebp.la $(PNG_LIBS) $(JPEG_LIBS)
cwebp_SOURCES = cwebp.c stopwatch.h cwebp_SOURCES = cwebp.c stopwatch.h
cwebp_CPPFLAGS = $(AM_CPPFLAGS) $(PNG_INCLUDES) $(JPEG_INCLUDES) $(USE_EXPERIMENTAL_CODE) cwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
cwebp_LDADD = ../src/libwebp.la $(PNG_LIBS) $(JPEG_LIBS) cwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
cwebp_LDADD = ../src/libwebp.la $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS)
webpmux_SOURCES = webpmux.c
webpmux_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
webpmux_LDADD = libexampleutil.la ../src/mux/libwebpmux.la ../src/libwebp.la

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -27,6 +27,10 @@
#include <jpeglib.h> #include <jpeglib.h>
#endif #endif
#ifdef WEBP_HAVE_TIFF
#include <tiffio.h>
#endif
#ifdef HAVE_WINCODEC_H #ifdef HAVE_WINCODEC_H
#ifdef __MINGW32__ #ifdef __MINGW32__
#define INITGUID // Without this GUIDs are declared extern and fail to link #define INITGUID // Without this GUIDs are declared extern and fail to link
@ -38,35 +42,35 @@
#include <shlwapi.h> #include <shlwapi.h>
#include <windows.h> #include <windows.h>
#include <wincodec.h> #include <wincodec.h>
#ifndef GUID_WICPixelFormat24bppRGB
// From Microsoft SDK 7.0a
DEFINE_GUID(GUID_WICPixelFormat24bppRGB,
0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
#endif
#ifndef GUID_WICPixelFormat32bppRGBA
DEFINE_GUID(GUID_WICPixelFormat32bppRGBA,
0xf5c7ad2d, 0x6a8d, 0x43dd, 0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
#endif
#endif /* HAVE_WINCODEC_H */ #endif /* HAVE_WINCODEC_H */
#include "webp/encode.h" #include "webp/encode.h"
#include "stopwatch.h" #include "./stopwatch.h"
#ifndef WEBP_DLL #ifndef WEBP_DLL
extern void* VP8GetCPUInfo; // opaque forward declaration. #if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif #endif
extern void* VP8GetCPUInfo; // opaque forward declaration.
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
#endif // WEBP_DLL
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static int verbose = 0; static int verbose = 0;
static int ReadYUV(FILE* in_file, WebPPicture* const pic) { static int ReadYUV(FILE* in_file, WebPPicture* const pic) {
const int use_argb = pic->use_argb;
const int uv_width = (pic->width + 1) / 2; const int uv_width = (pic->width + 1) / 2;
const int uv_height = (pic->height + 1) / 2; const int uv_height = (pic->height + 1) / 2;
int y; int y;
int ok = 0; int ok = 0;
pic->use_argb = 0;
if (!WebPPictureAlloc(pic)) return ok; if (!WebPPictureAlloc(pic)) return ok;
for (y = 0; y < pic->height; ++y) { for (y = 0; y < pic->height; ++y) {
@ -83,6 +87,7 @@ static int ReadYUV(FILE* in_file, WebPPicture* const pic) {
goto End; goto End;
} }
ok = 1; ok = 1;
if (use_argb) ok = WebPPictureYUVAToARGB(pic);
End: End:
return ok; return ok;
@ -96,10 +101,14 @@ static int ReadYUV(FILE* in_file, WebPPicture* const pic) {
{ \ { \
hr = (fn); \ hr = (fn); \
if (FAILED(hr) && verbose) \ if (FAILED(hr) && verbose) \
printf(#fn " failed %08x\n", hr); \ fprintf(stderr, #fn " failed %08x\n", hr); \
} \ } \
} while (0) } while (0)
// modified version of DEFINE_GUID from guiddef.h.
#define WEBP_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
#ifdef __cplusplus #ifdef __cplusplus
#define MAKE_REFGUID(x) (x) #define MAKE_REFGUID(x) (x)
#else #else
@ -110,7 +119,7 @@ static HRESULT OpenInputStream(const char* filename, IStream** ppStream) {
HRESULT hr = S_OK; HRESULT hr = S_OK;
IFS(SHCreateStreamOnFileA(filename, STGM_READ, ppStream)); IFS(SHCreateStreamOnFileA(filename, STGM_READ, ppStream));
if (FAILED(hr)) if (FAILED(hr))
printf("Error opening input file %s (%08x)\n", filename, hr); fprintf(stderr, "Error opening input file %s (%08x)\n", filename, hr);
return hr; return hr;
} }
@ -123,7 +132,7 @@ static HRESULT ReadPictureWithWIC(const char* filename,
IWICBitmapDecoder* pDecoder = NULL; IWICBitmapDecoder* pDecoder = NULL;
IStream* pStream = NULL; IStream* pStream = NULL;
UINT frameCount = 0; UINT frameCount = 0;
UINT width, height = 0; UINT width = 0, height = 0;
BYTE* rgb = NULL; BYTE* rgb = NULL;
WICPixelFormatGUID srcPixelFormat = { 0 }; WICPixelFormatGUID srcPixelFormat = { 0 };
GUID srcContainerFormat = { 0 }; GUID srcContainerFormat = { 0 };
@ -134,15 +143,28 @@ static HRESULT ReadPictureWithWIC(const char* filename,
}; };
int has_alpha = 0; int has_alpha = 0;
int i, stride; int i, stride;
// From Microsoft SDK 7.0a
// Create local copies for compatibility when building against earlier
// versions of the SDK.
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_,
0x6fddc324, 0x4e03, 0x4bfe,
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
0xf5c7ad2d, 0x6a8d, 0x43dd,
0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_,
0x6fddc324, 0x4e03, 0x4bfe,
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f);
IFS(CoInitialize(NULL)); IFS(CoInitialize(NULL));
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL, IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
CLSCTX_INPROC_SERVER, MAKE_REFGUID(IID_IWICImagingFactory), CLSCTX_INPROC_SERVER, MAKE_REFGUID(IID_IWICImagingFactory),
(LPVOID*)&pFactory)); (LPVOID*)&pFactory));
if (hr == REGDB_E_CLASSNOTREG) { if (hr == REGDB_E_CLASSNOTREG) {
printf("Couldn't access Windows Imaging Component (are you running \n"); fprintf(stderr,
printf("Windows XP SP3 or newer?). Most formats not available.\n"); "Couldn't access Windows Imaging Component (are you running "
printf("Use -s for the available YUV input.\n"); "Windows XP SP3 or newer?). Most formats not available. "
"Use -s for the available YUV input.\n");
} }
// Prepare for image decoding. // Prepare for image decoding.
IFS(OpenInputStream(filename, &pStream)); IFS(OpenInputStream(filename, &pStream));
@ -150,7 +172,7 @@ static HRESULT ReadPictureWithWIC(const char* filename,
WICDecodeMetadataCacheOnDemand, &pDecoder)); WICDecodeMetadataCacheOnDemand, &pDecoder));
IFS(IWICBitmapDecoder_GetFrameCount(pDecoder, &frameCount)); IFS(IWICBitmapDecoder_GetFrameCount(pDecoder, &frameCount));
if (SUCCEEDED(hr) && frameCount == 0) { if (SUCCEEDED(hr) && frameCount == 0) {
printf("No frame found in input file.\n"); fprintf(stderr, "No frame found in input file.\n");
hr = E_FAIL; hr = E_FAIL;
} }
IFS(IWICBitmapDecoder_GetFrame(pDecoder, 0, &pFrame)); IFS(IWICBitmapDecoder_GetFrame(pDecoder, 0, &pFrame));
@ -161,10 +183,13 @@ static HRESULT ReadPictureWithWIC(const char* filename,
for (i = 0; for (i = 0;
has_alpha && i < sizeof(alphaContainers)/sizeof(alphaContainers[0]); has_alpha && i < sizeof(alphaContainers)/sizeof(alphaContainers[0]);
++i) { ++i) {
if (IsEqualGUID(&srcContainerFormat, alphaContainers[i])) { if (IsEqualGUID(MAKE_REFGUID(srcContainerFormat),
MAKE_REFGUID(*alphaContainers[i]))) {
has_alpha = has_alpha =
IsEqualGUID(&srcPixelFormat, &GUID_WICPixelFormat32bppRGBA) || IsEqualGUID(MAKE_REFGUID(srcPixelFormat),
IsEqualGUID(&srcPixelFormat, &GUID_WICPixelFormat32bppBGRA); MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
IsEqualGUID(MAKE_REFGUID(srcPixelFormat),
MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_));
break; break;
} }
} }
@ -172,8 +197,8 @@ static HRESULT ReadPictureWithWIC(const char* filename,
// Prepare for pixel format conversion (if necessary). // Prepare for pixel format conversion (if necessary).
IFS(IWICImagingFactory_CreateFormatConverter(pFactory, &pConverter)); IFS(IWICImagingFactory_CreateFormatConverter(pFactory, &pConverter));
IFS(IWICFormatConverter_Initialize(pConverter, (IWICBitmapSource*)pFrame, IFS(IWICFormatConverter_Initialize(pConverter, (IWICBitmapSource*)pFrame,
has_alpha ? MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA) has_alpha ? MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)
: MAKE_REFGUID(GUID_WICPixelFormat24bppRGB), : MAKE_REFGUID(GUID_WICPixelFormat24bppRGB_),
WICBitmapDitherTypeNone, WICBitmapDitherTypeNone,
NULL, 0.0, WICBitmapPaletteTypeCustom)); NULL, 0.0, WICBitmapPaletteTypeCustom));
@ -191,11 +216,6 @@ static HRESULT ReadPictureWithWIC(const char* filename,
// WebP conversion. // WebP conversion.
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
int ok; int ok;
#ifdef WEBP_EXPERIMENTAL_FEATURES
if (has_alpha) {
pic->colorspace |= WEBP_CSP_ALPHA_BIT;
}
#endif
pic->width = width; pic->width = width;
pic->height = height; pic->height = height;
ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, stride) ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, stride)
@ -203,6 +223,11 @@ static HRESULT ReadPictureWithWIC(const char* filename,
if (!ok) if (!ok)
hr = E_FAIL; hr = E_FAIL;
} }
if (SUCCEEDED(hr)) {
if (has_alpha && keep_alpha == 2) {
WebPCleanupTransparentArea(pic);
}
}
// Cleanup. // Cleanup.
if (pConverter != NULL) IUnknown_Release(pConverter); if (pConverter != NULL) IUnknown_Release(pConverter);
@ -262,7 +287,7 @@ static int ReadJPEG(FILE* in_file, WebPPicture* const pic) {
dinfo.err = jpeg_std_error(&jerr.pub); dinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit; jerr.pub.error_exit = my_error_exit;
if (setjmp (jerr.setjmp_buffer)) { if (setjmp(jerr.setjmp_buffer)) {
Error: Error:
jpeg_destroy_decompress(&dinfo); jpeg_destroy_decompress(&dinfo);
goto End; goto End;
@ -306,8 +331,8 @@ static int ReadJPEG(FILE* in_file, WebPPicture* const pic) {
row_ptr += stride; row_ptr += stride;
} }
jpeg_finish_decompress (&dinfo); jpeg_finish_decompress(&dinfo);
jpeg_destroy_decompress (&dinfo); jpeg_destroy_decompress(&dinfo);
// WebP conversion. // WebP conversion.
pic->width = width; pic->width = width;
@ -325,8 +350,8 @@ static int ReadJPEG(FILE* in_file, WebPPicture* const pic) {
static int ReadJPEG(FILE* in_file, WebPPicture* const pic) { static int ReadJPEG(FILE* in_file, WebPPicture* const pic) {
(void)in_file; (void)in_file;
(void)pic; (void)pic;
printf("JPEG support not compiled. Please install the libjpeg development " fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
"package before building.\n"); "development package before building.\n");
return 0; return 0;
} }
#endif #endif
@ -358,7 +383,7 @@ static int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) {
if (setjmp(png_jmpbuf(png))) { if (setjmp(png_jmpbuf(png))) {
Error: Error:
png_destroy_read_struct(&png, NULL, NULL); png_destroy_read_struct(&png, NULL, NULL);
if (rgb) free(rgb); free(rgb);
goto End; goto End;
} }
@ -374,7 +399,8 @@ static int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) {
png_set_strip_16(png); png_set_strip_16(png);
png_set_packing(png); png_set_packing(png);
if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png);
if (color_type == PNG_COLOR_TYPE_GRAY) { if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
if (bit_depth < 8) { if (bit_depth < 8) {
png_set_expand_gray_1_2_4_to_8(png); png_set_expand_gray_1_2_4_to_8(png);
} }
@ -391,11 +417,6 @@ static int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) {
png_set_strip_alpha(png); png_set_strip_alpha(png);
has_alpha = 0; has_alpha = 0;
} }
#ifdef WEBP_EXPERIMENTAL_FEATURES
if (has_alpha) {
pic->colorspace |= WEBP_CSP_ALPHA_BIT;
}
#endif
num_passes = png_set_interlace_handling(png); num_passes = png_set_interlace_handling(png);
png_read_update_info(png, info); png_read_update_info(png, info);
@ -417,6 +438,10 @@ static int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) {
: WebPPictureImportRGB(pic, rgb, stride); : WebPPictureImportRGB(pic, rgb, stride);
free(rgb); free(rgb);
if (ok && has_alpha && keep_alpha == 2) {
WebPCleanupTransparentArea(pic);
}
End: End:
return ok; return ok;
} }
@ -425,16 +450,80 @@ static int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) {
(void)in_file; (void)in_file;
(void)pic; (void)pic;
(void)keep_alpha; (void)keep_alpha;
printf("PNG support not compiled. Please install the libpng development " fprintf(stderr, "PNG support not compiled. Please install the libpng "
"package before building.\n"); "development package before building.\n");
return 0;
}
#endif
#ifdef WEBP_HAVE_TIFF
static int ReadTIFF(const char* const filename,
WebPPicture* const pic, int keep_alpha) {
TIFF* const tif = TIFFOpen(filename, "r");
uint32 width, height;
uint32* raster;
int ok = 0;
int dircount = 1;
if (tif == NULL) {
fprintf(stderr, "Error! Cannot open TIFF file '%s'\n", filename);
return 0;
}
while (TIFFReadDirectory(tif)) ++dircount;
if (dircount > 1) {
fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n"
"Only the first will be used, %d will be ignored.\n",
dircount - 1);
}
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
raster = (uint32*)_TIFFmalloc(width * height * sizeof(*raster));
if (raster != NULL) {
if (TIFFReadRGBAImageOriented(tif, width, height, raster,
ORIENTATION_TOPLEFT, 1)) {
const int stride = width * sizeof(*raster);
pic->width = width;
pic->height = height;
// TIFF data is ABGR
#ifdef __BIG_ENDIAN__
TIFFSwabArrayOfLong(raster, width * height);
#endif
ok = keep_alpha
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride)
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride);
}
_TIFFfree(raster);
} else {
fprintf(stderr, "Error allocating TIFF RGBA memory!\n");
}
if (ok && keep_alpha == 2) {
WebPCleanupTransparentArea(pic);
}
TIFFClose(tif);
return ok;
}
#else
static int ReadTIFF(const char* const filename,
WebPPicture* const pic, int keep_alpha) {
(void)filename;
(void)pic;
(void)keep_alpha;
fprintf(stderr, "TIFF support not compiled. Please install the libtiff "
"development package before building.\n");
return 0; return 0;
} }
#endif #endif
typedef enum { typedef enum {
PNG = 0, PNG_ = 0,
JPEG, JPEG_,
UNSUPPORTED, TIFF_, // 'TIFF' clashes with libtiff
UNSUPPORTED
} InputFileFormat; } InputFileFormat;
static InputFileFormat GetImageType(FILE* in_file) { static InputFileFormat GetImageType(FILE* in_file) {
@ -449,9 +538,11 @@ static InputFileFormat GetImageType(FILE* in_file) {
magic = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; magic = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
if (magic == 0x89504E47U) { if (magic == 0x89504E47U) {
format = PNG; format = PNG_;
} else if (magic >= 0xFFD8FF00U && magic <= 0xFFD8FFFFU) { } else if (magic >= 0xFFD8FF00U && magic <= 0xFFD8FFFFU) {
format = JPEG; format = JPEG_;
} else if (magic == 0x49492A00 || magic == 0x4D4D002A) {
format = TIFF_;
} }
return format; return format;
} }
@ -468,10 +559,12 @@ static int ReadPicture(const char* const filename, WebPPicture* const pic,
if (pic->width == 0 || pic->height == 0) { if (pic->width == 0 || pic->height == 0) {
// If no size specified, try to decode it as PNG/JPEG (as appropriate). // If no size specified, try to decode it as PNG/JPEG (as appropriate).
const InputFileFormat format = GetImageType(in_file); const InputFileFormat format = GetImageType(in_file);
if (format == PNG) { if (format == PNG_) {
ok = ReadPNG(in_file, pic, keep_alpha); ok = ReadPNG(in_file, pic, keep_alpha);
} else if (format == JPEG) { } else if (format == JPEG_) {
ok = ReadJPEG(in_file, pic); ok = ReadJPEG(in_file, pic);
} else if (format == TIFF_) {
ok = ReadTIFF(filename, pic, keep_alpha);
} }
} else { } else {
// If image size is specified, infer it as YUV format. // If image size is specified, infer it as YUV format.
@ -502,7 +595,7 @@ static void PrintByteCount(const int bytes[4], int total_size,
total += bytes[s]; total += bytes[s];
if (totals) totals[s] += bytes[s]; if (totals) totals[s] += bytes[s];
} }
fprintf(stderr,"| %7d (%.1f%%)\n", total, 100.f * total / total_size); fprintf(stderr, "| %7d (%.1f%%)\n", total, 100.f * total / total_size);
} }
static void PrintPercents(const int counts[4], int total) { static void PrintPercents(const int counts[4], int total) {
@ -510,7 +603,7 @@ static void PrintPercents(const int counts[4], int total) {
for (s = 0; s < 4; ++s) { for (s = 0; s < 4; ++s) {
fprintf(stderr, "| %2d%%", 100 * counts[s] / total); fprintf(stderr, "| %2d%%", 100 * counts[s] / total);
} }
fprintf(stderr,"| %7d\n", total); fprintf(stderr, "| %7d\n", total);
} }
static void PrintValues(const int values[4]) { static void PrintValues(const int values[4]) {
@ -518,20 +611,58 @@ static void PrintValues(const int values[4]) {
for (s = 0; s < 4; ++s) { for (s = 0; s < 4; ++s) {
fprintf(stderr, "| %7d ", values[s]); fprintf(stderr, "| %7d ", values[s]);
} }
fprintf(stderr,"|\n"); fprintf(stderr, "|\n");
} }
static void PrintExtraInfo(const WebPPicture* const pic, int short_output) { static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
const char* const description) {
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
description, stats->lossless_size);
if (stats->lossless_features) {
fprintf(stderr, " * Lossless features used:");
if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
if (stats->lossless_features & 2) fprintf(stderr, " CROSS-COLOR-TRANSFORM");
if (stats->lossless_features & 4) fprintf(stderr, " SUBTRACT-GREEN");
if (stats->lossless_features & 8) fprintf(stderr, " PALETTE");
fprintf(stderr, "\n");
}
fprintf(stderr, " * Precision Bits: histogram=%d transform=%d cache=%d\n",
stats->histogram_bits, stats->transform_bits, stats->cache_bits);
if (stats->palette_size > 0) {
fprintf(stderr, " * Palette size: %d\n", stats->palette_size);
}
}
static void PrintExtraInfoLossless(const WebPPicture* const pic,
int short_output,
const char* const file_name) {
const WebPAuxStats* const stats = pic->stats; const WebPAuxStats* const stats = pic->stats;
if (short_output) { if (short_output) {
fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]); fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
} else{ } else {
fprintf(stderr, "File: %s\n", file_name);
fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
fprintf(stderr, "Output: %d bytes\n", stats->coded_size);
PrintFullLosslessInfo(stats, "ARGB");
}
}
static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
const char* const file_name) {
const WebPAuxStats* const stats = pic->stats;
if (short_output) {
fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
} else {
const int num_i4 = stats->block_count[0]; const int num_i4 = stats->block_count[0];
const int num_i16 = stats->block_count[1]; const int num_i16 = stats->block_count[1];
const int num_skip = stats->block_count[2]; const int num_skip = stats->block_count[2];
const int total = num_i4 + num_i16; const int total = num_i4 + num_i16;
fprintf(stderr, fprintf(stderr, "File: %s\n", file_name);
"%7d bytes Y-U-V-All-PSNR %2.2f %2.2f %2.2f %2.2f dB\n", fprintf(stderr, "Dimension: %d x %d%s\n",
pic->width, pic->height,
stats->alpha_data_size ? " (with alpha)" : "");
fprintf(stderr, "Output: "
"%d bytes Y-U-V-All-PSNR %2.2f %2.2f %2.2f %2.2f dB\n",
stats->coded_size, stats->coded_size,
stats->PSNR[0], stats->PSNR[1], stats->PSNR[2], stats->PSNR[3]); stats->PSNR[0], stats->PSNR[1], stats->PSNR[2], stats->PSNR[3]);
if (total > 0) { if (total > 0) {
@ -547,9 +678,9 @@ static void PrintExtraInfo(const WebPPicture* const pic, int short_output) {
100.f * stats->header_bytes[0] / stats->coded_size, 100.f * stats->header_bytes[0] / stats->coded_size,
stats->header_bytes[1], stats->header_bytes[1],
100.f * stats->header_bytes[1] / stats->coded_size); 100.f * stats->header_bytes[1] / stats->coded_size);
if (stats->alpha_data_size) { if (stats->alpha_data_size > 0) {
fprintf(stderr, " transparency: %6d\n", fprintf(stderr, " transparency: %6d (%.1f dB)\n",
stats->alpha_data_size); stats->alpha_data_size, stats->PSNR[4]);
} }
if (stats->layer_data_size) { if (stats->layer_data_size) {
fprintf(stderr, " enhancement: %6d\n", fprintf(stderr, " enhancement: %6d\n",
@ -575,8 +706,11 @@ static void PrintExtraInfo(const WebPPicture* const pic, int short_output) {
fprintf(stderr, " segments total: "); fprintf(stderr, " segments total: ");
PrintByteCount(totals, stats->coded_size, NULL); PrintByteCount(totals, stats->coded_size, NULL);
} }
if (stats->lossless_size > 0) {
PrintFullLosslessInfo(stats, "alpha");
}
} }
if (pic->extra_info) { if (pic->extra_info != NULL) {
const int mb_w = (pic->width + 15) / 16; const int mb_w = (pic->width + 15) / 16;
const int mb_h = (pic->height + 15) / 16; const int mb_h = (pic->height + 15) / 16;
const int type = pic->extra_info_type; const int type = pic->extra_info_type;
@ -615,10 +749,11 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
const int uv_width = (picture->width + 1) / 2; const int uv_width = (picture->width + 1) / 2;
const int uv_height = (picture->height + 1) / 2; const int uv_height = (picture->height + 1) / 2;
const int stride = (picture->width + 1) & ~1; const int stride = (picture->width + 1) & ~1;
const int alpha_height = picture->a ? picture->height : 0; const int alpha_height =
WebPPictureHasTransparency(picture) ? picture->height : 0;
const int height = picture->height + uv_height + alpha_height; const int height = picture->height + uv_height + alpha_height;
FILE* const f = fopen(PGM_name, "wb"); FILE* const f = fopen(PGM_name, "wb");
if (!f) return 0; if (f == NULL) return 0;
fprintf(f, "P5\n%d %d\n255\n", stride, height); fprintf(f, "P5\n%d %d\n255\n", stride, height);
for (y = 0; y < picture->height; ++y) { for (y = 0; y < picture->height; ++y) {
if (fwrite(picture->y + y * picture->y_stride, picture->width, 1, f) != 1) if (fwrite(picture->y + y * picture->y_stride, picture->width, 1, f) != 1)
@ -642,6 +777,15 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static int ProgressReport(int percent, const WebPPicture* const picture) {
printf("[%s]: %3d %% \r",
(char*)picture->user_data, percent);
fflush(stdout);
return 1; // all ok
}
//------------------------------------------------------------------------------
static void HelpShort(void) { static void HelpShort(void) {
printf("Usage:\n\n"); printf("Usage:\n\n");
printf(" cwebp [options] -q quality input.png -o output.webp\n\n"); printf(" cwebp [options] -q quality input.png -o output.webp\n\n");
@ -654,7 +798,7 @@ static void HelpLong(void) {
printf("Usage:\n"); printf("Usage:\n");
printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n"); printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n");
printf("If input size (-s) for an image is not specified, " printf("If input size (-s) for an image is not specified, "
"it is assumed to be a PNG or JPEG file.\n"); "it is assumed to be a PNG, JPEG or TIFF file.\n");
#ifdef HAVE_WINCODEC_H #ifdef HAVE_WINCODEC_H
printf("Windows builds can take as input any of the files handled by WIC\n"); printf("Windows builds can take as input any of the files handled by WIC\n");
#endif #endif
@ -662,6 +806,8 @@ static void HelpLong(void) {
printf(" -h / -help ............ short help\n"); printf(" -h / -help ............ short help\n");
printf(" -H / -longhelp ........ long help\n"); printf(" -H / -longhelp ........ long help\n");
printf(" -q <float> ............. quality factor (0:small..100:big)\n"); printf(" -q <float> ............. quality factor (0:small..100:big)\n");
printf(" -alpha_q <int> ......... Transparency-compression quality "
"(0..100).\n");
printf(" -preset <string> ....... Preset setting, one of:\n"); printf(" -preset <string> ....... Preset setting, one of:\n");
printf(" default, photo, picture,\n"); printf(" default, photo, picture,\n");
printf(" drawing, icon, text\n"); printf(" drawing, icon, text\n");
@ -681,10 +827,6 @@ static void HelpLong(void) {
printf(" -partition_limit <int> . limit quality to fit the 512k limit on\n"); printf(" -partition_limit <int> . limit quality to fit the 512k limit on\n");
printf(" " printf(" "
"the first partition (0=no degradation ... 100=full)\n"); "the first partition (0=no degradation ... 100=full)\n");
#ifdef WEBP_EXPERIMENTAL_FEATURES
printf(" -alpha_comp <int> ...... set the transparency-compression\n");
printf(" -noalpha ............... discard any transparency information.\n");
#endif
printf(" -pass <int> ............ analysis pass number (1..10)\n"); printf(" -pass <int> ............ analysis pass number (1..10)\n");
printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n"); printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
printf(" -resize <w> <h> ........ resize picture (after any cropping)\n"); printf(" -resize <w> <h> ........ resize picture (after any cropping)\n");
@ -692,7 +834,17 @@ static void HelpLong(void) {
printf(" -444 / -422 / -gray ..... Change colorspace\n"); printf(" -444 / -422 / -gray ..... Change colorspace\n");
#endif #endif
printf(" -map <int> ............. print map of extra info.\n"); printf(" -map <int> ............. print map of extra info.\n");
printf(" -print_ssim ............ prints averaged SSIM distortion.\n");
printf(" -print_psnr ............ prints averaged PSNR distortion.\n");
printf(" -d <file.pgm> .......... dump the compressed output (PGM file).\n"); printf(" -d <file.pgm> .......... dump the compressed output (PGM file).\n");
printf(" -alpha_method <int> .... Transparency-compression method (0..1)\n");
printf(" -alpha_filter <string> . predictive filtering for alpha plane.\n");
printf(" One of: none, fast (default) or best.\n");
printf(" -alpha_cleanup ......... Clean RGB values in transparent area.\n");
printf(" -noalpha ............... discard any transparency information.\n");
printf(" -lossless .............. Encode image losslessly.\n");
printf(" -hint <string> ......... Specify image characteristics hint.\n");
printf(" One of: photo, picture or graph\n");
printf("\n"); printf("\n");
printf(" -short ................. condense printed message\n"); printf(" -short ................. condense printed message\n");
@ -703,6 +855,7 @@ static void HelpLong(void) {
#endif #endif
printf(" -v ..................... verbose, e.g. print encoding/decoding " printf(" -v ..................... verbose, e.g. print encoding/decoding "
"times\n"); "times\n");
printf(" -progress .............. report encoding progress\n");
printf("\n"); printf("\n");
printf("Experimental Options:\n"); printf("Experimental Options:\n");
printf(" -af .................... auto-adjust filter strength.\n"); printf(" -af .................... auto-adjust filter strength.\n");
@ -727,33 +880,36 @@ static const char* const kErrorMessages[] = {
"header bits using -partition_limit. More details are available " "header bits using -partition_limit. More details are available "
"in the manual (`man cwebp`)", "in the manual (`man cwebp`)",
"PARTITION_OVERFLOW: Partition is too big to fit 16M", "PARTITION_OVERFLOW: Partition is too big to fit 16M",
"BAD_WRITE: Picture writer returned an I/O error" "BAD_WRITE: Picture writer returned an I/O error",
"FILE_TOO_BIG: File would be too big to fit in 4G" "FILE_TOO_BIG: File would be too big to fit in 4G",
"USER_ABORT: encoding abort requested by user"
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int main(int argc, const char *argv[]) { int main(int argc, const char *argv[]) {
int return_value = -1;
const char *in_file = NULL, *out_file = NULL, *dump_file = NULL; const char *in_file = NULL, *out_file = NULL, *dump_file = NULL;
FILE *out = NULL; FILE *out = NULL;
int c; int c;
int short_output = 0; int short_output = 0;
int quiet = 0; int quiet = 0;
int keep_alpha = 0; int keep_alpha = 1;
int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0; int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
int resize_w = 0, resize_h = 0; int resize_w = 0, resize_h = 0;
int show_progress = 0;
WebPPicture picture; WebPPicture picture;
int print_distortion = 0; // 1=PSNR, 2=SSIM
WebPPicture original_picture; // when PSNR or SSIM is requested
WebPConfig config; WebPConfig config;
WebPAuxStats stats; WebPAuxStats stats;
Stopwatch stop_watch; Stopwatch stop_watch;
#ifdef WEBP_EXPERIMENTAL_FEATURES if (!WebPPictureInit(&picture) ||
keep_alpha = 1; !WebPPictureInit(&original_picture) ||
#endif !WebPConfigInit(&config)) {
if (!WebPPictureInit(&picture) || !WebPConfigInit(&config)) {
fprintf(stderr, "Error! Version mismatch!\n"); fprintf(stderr, "Error! Version mismatch!\n");
goto Error; return -1;
} }
if (argc == 1) { if (argc == 1) {
@ -773,6 +929,12 @@ int main(int argc, const char *argv[]) {
} else if (!strcmp(argv[c], "-d") && c < argc - 1) { } else if (!strcmp(argv[c], "-d") && c < argc - 1) {
dump_file = argv[++c]; dump_file = argv[++c];
config.show_compressed = 1; config.show_compressed = 1;
} else if (!strcmp(argv[c], "-print_ssim")) {
config.show_compressed = 1;
print_distortion = 2;
} else if (!strcmp(argv[c], "-print_psnr")) {
config.show_compressed = 1;
print_distortion = 1;
} else if (!strcmp(argv[c], "-short")) { } else if (!strcmp(argv[c], "-short")) {
short_output++; short_output++;
} else if (!strcmp(argv[c], "-s") && c < argc - 2) { } else if (!strcmp(argv[c], "-s") && c < argc - 2) {
@ -782,6 +944,41 @@ int main(int argc, const char *argv[]) {
config.method = strtol(argv[++c], NULL, 0); config.method = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-q") && c < argc - 1) { } else if (!strcmp(argv[c], "-q") && c < argc - 1) {
config.quality = (float)strtod(argv[++c], NULL); config.quality = (float)strtod(argv[++c], NULL);
} else if (!strcmp(argv[c], "-alpha_q") && c < argc - 1) {
config.alpha_quality = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
config.alpha_compression = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-alpha_cleanup")) {
keep_alpha = keep_alpha ? 2 : 0;
} else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
++c;
if (!strcmp(argv[c], "none")) {
config.alpha_filtering = 0;
} else if (!strcmp(argv[c], "fast")) {
config.alpha_filtering = 1;
} else if (!strcmp(argv[c], "best")) {
config.alpha_filtering = 2;
} else {
fprintf(stderr, "Error! Unrecognized alpha filter: %s\n", argv[c]);
goto Error;
}
} else if (!strcmp(argv[c], "-noalpha")) {
keep_alpha = 0;
} else if (!strcmp(argv[c], "-lossless")) {
config.lossless = 1;
picture.use_argb = 1;
} else if (!strcmp(argv[c], "-hint") && c < argc - 1) {
++c;
if (!strcmp(argv[c], "photo")) {
config.image_hint = WEBP_HINT_PHOTO;
} else if (!strcmp(argv[c], "picture")) {
config.image_hint = WEBP_HINT_PICTURE;
} else if (!strcmp(argv[c], "graph")) {
config.image_hint = WEBP_HINT_GRAPH;
} else {
fprintf(stderr, "Error! Unrecognized image hint: %s\n", argv[c]);
goto Error;
}
} else if (!strcmp(argv[c], "-size") && c < argc - 1) { } else if (!strcmp(argv[c], "-size") && c < argc - 1) {
config.target_size = strtol(argv[++c], NULL, 0); config.target_size = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-psnr") && c < argc - 1) { } else if (!strcmp(argv[c], "-psnr") && c < argc - 1) {
@ -804,12 +1001,6 @@ int main(int argc, const char *argv[]) {
config.segments = strtol(argv[++c], NULL, 0); config.segments = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) { } else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) {
config.partition_limit = strtol(argv[++c], NULL, 0); config.partition_limit = strtol(argv[++c], NULL, 0);
#ifdef WEBP_EXPERIMENTAL_FEATURES
} else if (!strcmp(argv[c], "-alpha_comp") && c < argc - 1) {
config.alpha_compression = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-noalpha")) {
keep_alpha = 0;
#endif
} else if (!strcmp(argv[c], "-map") && c < argc - 1) { } else if (!strcmp(argv[c], "-map") && c < argc - 1) {
picture.extra_info_type = strtol(argv[++c], NULL, 0); picture.extra_info_type = strtol(argv[++c], NULL, 0);
#ifdef WEBP_EXPERIMENTAL_FEATURES #ifdef WEBP_EXPERIMENTAL_FEATURES
@ -838,6 +1029,8 @@ int main(int argc, const char *argv[]) {
printf("%d.%d.%d\n", printf("%d.%d.%d\n",
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
return 0; return 0;
} else if (!strcmp(argv[c], "-progress")) {
show_progress = 1;
} else if (!strcmp(argv[c], "-quiet")) { } else if (!strcmp(argv[c], "-quiet")) {
quiet = 1; quiet = 1;
} else if (!strcmp(argv[c], "-preset") && c < argc - 1) { } else if (!strcmp(argv[c], "-preset") && c < argc - 1) {
@ -873,6 +1066,24 @@ int main(int argc, const char *argv[]) {
in_file = argv[c]; in_file = argv[c];
} }
} }
if (in_file == NULL) {
fprintf(stderr, "No input file specified!\n");
HelpShort();
goto Error;
}
// Check for unsupported command line options for lossless mode and log
// warning for such options.
if (!quiet && config.lossless == 1) {
if (config.target_size > 0 || config.target_PSNR > 0) {
fprintf(stderr, "Encoding for specified size or PSNR is not supported"
" for lossless encoding. Ignoring such option(s)!\n");
}
if (config.partition_limit > 0) {
fprintf(stderr, "Partition limit option is not required for lossless"
" encoding. Ignoring this option!\n");
}
}
if (!WebPValidateConfig(&config)) { if (!WebPValidateConfig(&config)) {
fprintf(stderr, "Error! Invalid configuration.\n"); fprintf(stderr, "Error! Invalid configuration.\n");
@ -884,9 +1095,11 @@ int main(int argc, const char *argv[]) {
StopwatchReadAndReset(&stop_watch); StopwatchReadAndReset(&stop_watch);
} }
if (!ReadPicture(in_file, &picture, keep_alpha)) { if (!ReadPicture(in_file, &picture, keep_alpha)) {
fprintf(stderr, "Error! Cannot read input picture\n"); fprintf(stderr, "Error! Cannot read input picture file '%s'\n", in_file);
goto Error; goto Error;
} }
picture.progress_hook = (show_progress && !quiet) ? ProgressReport : NULL;
if (verbose) { if (verbose) {
const double time = StopwatchReadAndReset(&stop_watch); const double time = StopwatchReadAndReset(&stop_watch);
fprintf(stderr, "Time to read input: %.3fs\n", time); fprintf(stderr, "Time to read input: %.3fs\n", time);
@ -895,7 +1108,7 @@ int main(int argc, const char *argv[]) {
// Open the output // Open the output
if (out_file) { if (out_file) {
out = fopen(out_file, "wb"); out = fopen(out_file, "wb");
if (!out) { if (out == NULL) {
fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file); fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
goto Error; goto Error;
} else { } else {
@ -912,15 +1125,21 @@ int main(int argc, const char *argv[]) {
fprintf(stderr, "be performed, but its results discarded.\n\n"); fprintf(stderr, "be performed, but its results discarded.\n\n");
} }
} }
picture.stats = &stats; if (!quiet) {
picture.stats = &stats;
picture.user_data = (void*)in_file;
}
// Compress // Compress
if (verbose) { if (verbose) {
StopwatchReadAndReset(&stop_watch); StopwatchReadAndReset(&stop_watch);
} }
if (crop != 0 && !WebPPictureCrop(&picture, crop_x, crop_y, crop_w, crop_h)) { if (crop != 0) {
fprintf(stderr, "Error! Cannot crop picture\n"); // We use self-cropping using a view.
goto Error; if (!WebPPictureView(&picture, crop_x, crop_y, crop_w, crop_h, &picture)) {
fprintf(stderr, "Error! Cannot crop picture\n");
goto Error;
}
} }
if ((resize_w | resize_h) > 0) { if ((resize_w | resize_h) > 0) {
if (!WebPPictureRescale(&picture, resize_w, resize_h)) { if (!WebPPictureRescale(&picture, resize_w, resize_h)) {
@ -931,6 +1150,9 @@ int main(int argc, const char *argv[]) {
if (picture.extra_info_type > 0) { if (picture.extra_info_type > 0) {
AllocExtraInfo(&picture); AllocExtraInfo(&picture);
} }
if (print_distortion > 0) { // Save original picture for later comparison
WebPPictureCopy(&picture, &original_picture);
}
if (!WebPEncode(&config, &picture)) { if (!WebPEncode(&config, &picture)) {
fprintf(stderr, "Error! Cannot encode picture as WebP\n"); fprintf(stderr, "Error! Cannot encode picture as WebP\n");
fprintf(stderr, "Error code: %d (%s)\n", fprintf(stderr, "Error code: %d (%s)\n",
@ -944,20 +1166,39 @@ int main(int argc, const char *argv[]) {
// Write info // Write info
if (dump_file) { if (dump_file) {
DumpPicture(&picture, dump_file); if (picture.use_argb) {
fprintf(stderr, "Warning: can't dump file (-d option) in lossless mode.");
} else if (!DumpPicture(&picture, dump_file)) {
fprintf(stderr, "Warning, couldn't dump picture %s\n", dump_file);
}
} }
if (!quiet) { if (!quiet) {
PrintExtraInfo(&picture, short_output); if (config.lossless) {
PrintExtraInfoLossless(&picture, short_output, in_file);
} else {
PrintExtraInfoLossy(&picture, short_output, in_file);
}
} }
if (!quiet && !short_output && print_distortion > 0) { // print distortion
float values[5];
WebPPictureDistortion(&picture, &original_picture,
(print_distortion == 1) ? 0 : 1, values);
fprintf(stderr, "%s: Y:%.2f U:%.2f V:%.2f A:%.2f Total:%.2f\n",
(print_distortion == 1) ? "PSNR" : "SSIM",
values[0], values[1], values[2], values[3], values[4]);
}
return_value = 0;
Error: Error:
free(picture.extra_info); free(picture.extra_info);
WebPPictureFree(&picture); WebPPictureFree(&picture);
WebPPictureFree(&original_picture);
if (out != NULL) { if (out != NULL) {
fclose(out); fclose(out);
} }
return 0; return return_value;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -38,22 +38,28 @@
#endif #endif
#include "webp/decode.h" #include "webp/decode.h"
#include "stopwatch.h" #include "./example_util.h"
#include "./stopwatch.h"
static int verbose = 0;
#ifndef WEBP_DLL
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
static int verbose = 0;
#ifndef WEBP_DLL
extern void* VP8GetCPUInfo; // opaque forward declaration. extern void* VP8GetCPUInfo; // opaque forward declaration.
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif #endif
#endif // WEBP_DLL
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Output types // Output types
typedef enum { typedef enum {
PNG = 0, PNG = 0,
PAM,
PPM, PPM,
PGM, PGM,
ALPHA_PLANE_ONLY // this is for experimenting only ALPHA_PLANE_ONLY // this is for experimenting only
@ -67,7 +73,7 @@ typedef enum {
{ \ { \
hr = (fn); \ hr = (fn); \
if (FAILED(hr) && verbose) \ if (FAILED(hr) && verbose) \
printf(#fn " failed %08x\n", hr); \ fprintf(stderr, #fn " failed %08x\n", hr); \
} \ } \
} while (0) } while (0)
@ -82,7 +88,7 @@ static HRESULT CreateOutputStream(const char* out_file_name,
HRESULT hr = S_OK; HRESULT hr = S_OK;
IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, ppStream)); IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, ppStream));
if (FAILED(hr)) if (FAILED(hr))
printf("Error opening output file %s (%08x)\n", out_file_name, hr); fprintf(stderr, "Error opening output file %s (%08x)\n", out_file_name, hr);
return hr; return hr;
} }
@ -102,9 +108,10 @@ static HRESULT WriteUsingWIC(const char* out_file_name, REFGUID container_guid,
CLSCTX_INPROC_SERVER, MAKE_REFGUID(IID_IWICImagingFactory), CLSCTX_INPROC_SERVER, MAKE_REFGUID(IID_IWICImagingFactory),
(LPVOID*)&pFactory)); (LPVOID*)&pFactory));
if (hr == REGDB_E_CLASSNOTREG) { if (hr == REGDB_E_CLASSNOTREG) {
printf("Couldn't access Windows Imaging Component (are you running \n"); fprintf(stderr,
printf("Windows XP SP3 or newer?). PNG support not available.\n"); "Couldn't access Windows Imaging Component (are you running "
printf("Use -ppm or -pgm for available PPM and PGM formats.\n"); "Windows XP SP3 or newer?). PNG support not available. "
"Use -ppm or -pgm for available PPM and PGM formats.\n");
} }
IFS(CreateOutputStream(out_file_name, &pStream)); IFS(CreateOutputStream(out_file_name, &pStream));
IFS(IWICImagingFactory_CreateEncoder(pFactory, container_guid, NULL, IFS(IWICImagingFactory_CreateEncoder(pFactory, container_guid, NULL,
@ -185,28 +192,32 @@ static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
return 1; return 1;
} }
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG #else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
typedef uint32_t png_uint_32;
static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
(void)out_file; (void)out_file;
(void)buffer; (void)buffer;
printf("PNG support not compiled. Please install the libpng development " fprintf(stderr, "PNG support not compiled. Please install the libpng "
"package before building.\n"); "development package before building.\n");
printf("You can run with -ppm flag to decode in PPM format.\n"); fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
return 0; return 0;
} }
#endif #endif
static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer) { static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) {
const uint32_t width = buffer->width; const uint32_t width = buffer->width;
const uint32_t height = buffer->height; const uint32_t height = buffer->height;
const unsigned char* const rgb = buffer->u.RGBA.rgba; const unsigned char* const rgb = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride; const int stride = buffer->u.RGBA.stride;
const size_t bytes_per_px = alpha ? 4 : 3;
uint32_t y; uint32_t y;
fprintf(fout, "P6\n%d %d\n255\n", width, height);
if (alpha) {
fprintf(fout, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n"
"TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
} else {
fprintf(fout, "P6\n%d %d\n255\n", width, height);
}
for (y = 0; y < height; ++y) { for (y = 0; y < height; ++y) {
if (fwrite(rgb + y * stride, width, 3, fout) != 3) { if (fwrite(rgb + y * stride, width, bytes_per_px, fout) != bytes_per_px) {
return 0; return 0;
} }
} }
@ -286,8 +297,10 @@ static void SaveOutput(const WebPDecBuffer* const buffer,
#else #else
ok &= WritePNG(fout, buffer); ok &= WritePNG(fout, buffer);
#endif #endif
} else if (format == PAM) {
ok &= WritePPM(fout, buffer, 1);
} else if (format == PPM) { } else if (format == PPM) {
ok &= WritePPM(fout, buffer); ok &= WritePPM(fout, buffer, 0);
} else if (format == PGM) { } else if (format == PGM) {
ok &= WritePGM(fout, buffer); ok &= WritePGM(fout, buffer);
} else if (format == ALPHA_PLANE_ONLY) { } else if (format == ALPHA_PLANE_ONLY) {
@ -311,7 +324,8 @@ static void Help(void) {
printf("Usage: dwebp in_file [options] [-o out_file]\n\n" printf("Usage: dwebp in_file [options] [-o out_file]\n\n"
"Decodes the WebP image file to PNG format [Default]\n" "Decodes the WebP image file to PNG format [Default]\n"
"Use following options to convert into alternate image formats:\n" "Use following options to convert into alternate image formats:\n"
" -ppm ......... save the raw RGB samples as color PPM\n" " -pam ......... save the raw RGBA samples as a color PAM\n"
" -ppm ......... save the raw RGB samples as a color PPM\n"
" -pgm ......... save the raw YUV samples as a grayscale PGM\n" " -pgm ......... save the raw YUV samples as a grayscale PGM\n"
" file with IMC4 layout.\n" " file with IMC4 layout.\n"
" Other options are:\n" " Other options are:\n"
@ -321,9 +335,7 @@ static void Help(void) {
" -mt .......... use multi-threading\n" " -mt .......... use multi-threading\n"
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n" " -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
" -scale <w> <h> .......... scale the output (*after* any cropping)\n" " -scale <w> <h> .......... scale the output (*after* any cropping)\n"
#ifdef WEBP_EXPERIMENTAL_FEATURES
" -alpha ....... only save the alpha plane.\n" " -alpha ....... only save the alpha plane.\n"
#endif
" -h ....... this help message.\n" " -h ....... this help message.\n"
" -v ....... verbose (e.g. print encoding/decoding times)\n" " -v ....... verbose (e.g. print encoding/decoding times)\n"
#ifndef WEBP_DLL #ifndef WEBP_DLL
@ -364,12 +376,14 @@ int main(int argc, const char *argv[]) {
config.options.no_fancy_upsampling = 1; config.options.no_fancy_upsampling = 1;
} else if (!strcmp(argv[c], "-nofilter")) { } else if (!strcmp(argv[c], "-nofilter")) {
config.options.bypass_filtering = 1; config.options.bypass_filtering = 1;
} else if (!strcmp(argv[c], "-pam")) {
format = PAM;
} else if (!strcmp(argv[c], "-ppm")) { } else if (!strcmp(argv[c], "-ppm")) {
format = PPM; format = PPM;
} else if (!strcmp(argv[c], "-version")) { } else if (!strcmp(argv[c], "-version")) {
const int version = WebPGetDecoderVersion(); const int version = WebPGetDecoderVersion();
printf("%d.%d.%d\n", printf("%d.%d.%d\n",
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
return 0; return 0;
} else if (!strcmp(argv[c], "-pgm")) { } else if (!strcmp(argv[c], "-pgm")) {
format = PGM; format = PGM;
@ -392,7 +406,7 @@ int main(int argc, const char *argv[]) {
VP8GetCPUInfo = NULL; VP8GetCPUInfo = NULL;
#endif #endif
} else if (argv[c][0] == '-') { } else if (argv[c][0] == '-') {
printf("Unknown option '%s'\n", argv[c]); fprintf(stderr, "Unknown option '%s'\n", argv[c]);
Help(); Help();
return -1; return -1;
} else { } else {
@ -401,7 +415,7 @@ int main(int argc, const char *argv[]) {
} }
if (in_file == NULL) { if (in_file == NULL) {
printf("missing input file!!\n"); fprintf(stderr, "missing input file!!\n");
Help(); Help();
return -1; return -1;
} }
@ -410,31 +424,15 @@ int main(int argc, const char *argv[]) {
Stopwatch stop_watch; Stopwatch stop_watch;
VP8StatusCode status = VP8_STATUS_OK; VP8StatusCode status = VP8_STATUS_OK;
int ok; int ok;
uint32_t data_size = 0; size_t data_size = 0;
void* data = NULL; const uint8_t* data = NULL;
FILE* const in = fopen(in_file, "rb");
if (!in) { if (!ExUtilReadFile(in_file, &data, &data_size)) return -1;
fprintf(stderr, "cannot open input file '%s'\n", in_file);
return 1;
}
fseek(in, 0, SEEK_END);
data_size = ftell(in);
fseek(in, 0, SEEK_SET);
data = malloc(data_size);
ok = (fread(data, data_size, 1, in) == 1);
fclose(in);
if (!ok) {
fprintf(stderr, "Could not read %d bytes of data from file %s\n",
data_size, in_file);
free(data);
return -1;
}
if (verbose) if (verbose)
StopwatchReadAndReset(&stop_watch); StopwatchReadAndReset(&stop_watch);
status = WebPGetFeatures((const uint8_t*)data, data_size, bitstream); status = WebPGetFeatures(data, data_size, bitstream);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
goto end; goto end;
} }
@ -447,6 +445,9 @@ int main(int argc, const char *argv[]) {
output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB; output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
#endif #endif
break; break;
case PAM:
output_buffer->colorspace = MODE_RGBA;
break;
case PPM: case PPM:
output_buffer->colorspace = MODE_RGB; // drops alpha for PPM output_buffer->colorspace = MODE_RGB; // drops alpha for PPM
break; break;
@ -457,17 +458,17 @@ int main(int argc, const char *argv[]) {
output_buffer->colorspace = MODE_YUVA; output_buffer->colorspace = MODE_YUVA;
break; break;
default: default:
free(data); free((void*)data);
return -1; return -1;
} }
status = WebPDecode((const uint8_t*)data, data_size, &config); status = WebPDecode(data, data_size, &config);
if (verbose) { if (verbose) {
const double time = StopwatchReadAndReset(&stop_watch); const double time = StopwatchReadAndReset(&stop_watch);
printf("Time to decode picture: %.3fs\n", time); printf("Time to decode picture: %.3fs\n", time);
} }
end: end:
free(data); free((void*)data);
ok = (status == VP8_STATUS_OK); ok = (status == VP8_STATUS_OK);
if (!ok) { if (!ok) {
fprintf(stderr, "Decoding of %s failed.\n", in_file); fprintf(stderr, "Decoding of %s failed.\n", in_file);
@ -493,7 +494,3 @@ int main(int argc, const char *argv[]) {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

59
examples/example_util.c Normal file
View File

@ -0,0 +1,59 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Utility functions used by the example programs.
//
#include "./example_util.h"
#include <stdio.h>
#include <stdlib.h>
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
// -----------------------------------------------------------------------------
// File I/O
int ExUtilReadFile(const char* const file_name,
const uint8_t** data, size_t* data_size) {
int ok;
void* file_data;
size_t file_size;
FILE* in;
if (file_name == NULL || data == NULL || data_size == NULL) return 0;
*data = NULL;
*data_size = 0;
in = fopen(file_name, "rb");
if (in == NULL) {
fprintf(stderr, "cannot open input file '%s'\n", file_name);
return 0;
}
fseek(in, 0, SEEK_END);
file_size = ftell(in);
fseek(in, 0, SEEK_SET);
file_data = malloc(file_size);
if (file_data == NULL) return 0;
ok = (fread(file_data, file_size, 1, in) == 1);
fclose(in);
if (!ok) {
fprintf(stderr, "Could not read %zu bytes of data from file %s\n",
file_size, file_name);
free(file_data);
return 0;
}
*data = (uint8_t*)file_data;
*data_size = file_size;
return 1;
}
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

30
examples/example_util.h Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Utility functions used by the example programs.
//
#ifndef WEBP_EXAMPLES_EXAMPLE_UTIL_H_
#define WEBP_EXAMPLES_EXAMPLE_UTIL_H_
#include "webp/types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
// Allocates storage for entire file 'file_name' and returns contents and size
// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
// be deleted using free().
int ExUtilReadFile(const char* const file_name,
const uint8_t** data, size_t* data_size);
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
#endif // WEBP_EXAMPLES_EXAMPLE_UTIL_H_

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -12,12 +12,12 @@
#ifndef WEBP_EXAMPLES_STOPWATCH_H_ #ifndef WEBP_EXAMPLES_STOPWATCH_H_
#define WEBP_EXAMPLES_STOPWATCH_H_ #define WEBP_EXAMPLES_STOPWATCH_H_
#ifdef _WIN32 #if defined _WIN32 && !defined __GNUC__
#include <windows.h> #include <windows.h>
typedef LARGE_INTEGER Stopwatch; typedef LARGE_INTEGER Stopwatch;
static inline double StopwatchReadAndReset(Stopwatch* watch) { static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
const LARGE_INTEGER old_value = *watch; const LARGE_INTEGER old_value = *watch;
LARGE_INTEGER freq; LARGE_INTEGER freq;
if (!QueryPerformanceCounter(watch)) if (!QueryPerformanceCounter(watch))
@ -35,7 +35,7 @@ static inline double StopwatchReadAndReset(Stopwatch* watch) {
typedef struct timeval Stopwatch; typedef struct timeval Stopwatch;
static inline double StopwatchReadAndReset(Stopwatch* watch) { static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
const struct timeval old_value = *watch; const struct timeval old_value = *watch;
gettimeofday(watch, NULL); gettimeofday(watch, NULL);
return watch->tv_sec - old_value.tv_sec + return watch->tv_sec - old_value.tv_sec +

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

364
examples/vwebp.c Normal file
View File

@ -0,0 +1,364 @@
// Copyright 2011 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Simple WebP file viewer.
//
// Compiling on linux:
// sudo apt-get install libglut3-dev mesa-common-dev
// gcc -o vwebp vwebp.c -O3 -lwebp -lwebpmux -lglut -lGL -lpthread -lm
// Compiling on Mac + XCode:
// gcc -o vwebp vwebp.c -lwebp -lwebpmux -framework GLUT -framework OpenGL
//
// Author: Skal (pascal.massimino@gmail.com)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "webp/decode.h"
#include "webp/mux.h"
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#ifdef FREEGLUT
#include <GL/freeglut.h>
#endif
#endif
#include "./example_util.h"
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
static void Help(void);
// Unfortunate global variables. Gathered into a struct for comfort.
static struct {
int has_animation;
int done;
int decoding_error;
int print_info;
uint32_t flags;
int loop_count;
int frame_num;
int frame_max;
const char* file_name;
WebPData data;
WebPMux* mux;
WebPDecoderConfig* config;
const WebPDecBuffer* pic;
} kParams = {
0, 0, 0, 0, // has_animation, ...
0, 1, 1, 0, // flags, ...
NULL, { NULL, 0 }, // file_name, ...
NULL, NULL, NULL // mux, ...
};
static void ClearPreviousPic(void) {
WebPFreeDecBuffer((WebPDecBuffer*)kParams.pic);
kParams.pic = NULL;
}
static void ClearParams(void) {
ClearPreviousPic();
WebPDataClear(&kParams.data);
WebPMuxDelete(kParams.mux);
kParams.mux = NULL;
}
//------------------------------------------------------------------------------
// Callbacks
static void HandleKey(unsigned char key, int pos_x, int pos_y) {
(void)pos_x;
(void)pos_y;
if (key == 'q' || key == 'Q' || key == 27 /* Esc */) {
#ifdef FREEGLUT
glutLeaveMainLoop();
#else
ClearParams();
exit(0);
#endif
} else if (key == 'i') {
kParams.print_info = 1 - kParams.print_info;
glutPostRedisplay();
}
}
static void HandleReshape(int width, int height) {
// TODO(skal): proper handling of resize, esp. for large pictures.
// + key control of the zoom.
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
static void PrintString(const char* const text) {
void* const font = GLUT_BITMAP_9_BY_15;
int i;
for (i = 0; text[i]; ++i) {
glutBitmapCharacter(font, text[i]);
}
}
static void DrawCheckerBoard(void) {
const int square_size = 8; // must be a power of 2
int x, y;
GLint viewport[4]; // x, y, width, height
glPushMatrix();
glGetIntegerv(GL_VIEWPORT, viewport);
// shift to integer coordinates with (0,0) being top-left.
glOrtho(0, viewport[2], viewport[3], 0, -1, 1);
for (y = 0; y < viewport[3]; y += square_size) {
for (x = 0; x < viewport[2]; x += square_size) {
const GLubyte color = 128 + 64 * (!((x + y) & square_size));
glColor3ub(color, color, color);
glRecti(x, y, x + square_size, y + square_size);
}
}
glPopMatrix();
}
static void HandleDisplay(void) {
const WebPDecBuffer* pic = kParams.pic;
if (pic == NULL) return;
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
glPixelZoom(1, -1);
glRasterPos2f(-1, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4);
DrawCheckerBoard();
glDrawPixels(pic->width, pic->height,
GL_RGBA, GL_UNSIGNED_BYTE,
(GLvoid*)pic->u.RGBA.rgba);
if (kParams.print_info) {
char tmp[32];
glColor4f(0.0, 0.0, 0.0, 1.0);
glRasterPos2f(-0.95f, 0.90f);
PrintString(kParams.file_name);
snprintf(tmp, sizeof(tmp), "Dimension:%d x %d", pic->width, pic->height);
glColor4f(0.0, 0.0, 0.0, 1.0);
glRasterPos2f(-0.95f, 0.80f);
PrintString(tmp);
}
glPopMatrix();
glFlush();
}
static void StartDisplay(const WebPDecBuffer* const pic) {
glutInitDisplayMode(GLUT_RGBA);
glutInitWindowSize(pic->width, pic->height);
glutCreateWindow("WebP viewer");
glutDisplayFunc(HandleDisplay);
glutIdleFunc(NULL);
glutKeyboardFunc(HandleKey);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glClearColor(0.0, 0.0, 0.0, 0.0);
HandleReshape(pic->width, pic->height);
}
//------------------------------------------------------------------------------
// File decoding
static int Decode(const int frame_number, int* const duration) {
WebPDecoderConfig* const config = kParams.config;
WebPData *data, image_data;
int x_off = 0, y_off = 0;
WebPDecBuffer* const output_buffer = &config->output;
int ok = 0;
ClearPreviousPic();
if (kParams.has_animation) {
if (WebPMuxGetFrame(kParams.mux, frame_number, &image_data,
&x_off, &y_off, duration) != WEBP_MUX_OK) {
goto end;
}
if (x_off != 0 || y_off != 0) {
fprintf(stderr,
"Frame offsets not yet supported! Forcing offset to 0,0\n");
x_off = y_off = 0;
}
data = &image_data;
} else {
data = &kParams.data;
}
output_buffer->colorspace = MODE_RGBA;
ok = (WebPDecode(data->bytes_, data->size_, config) == VP8_STATUS_OK);
end:
if (!ok) {
fprintf(stderr, "Decoding of frame #%d failed!\n", frame_number);
} else {
kParams.pic = output_buffer;
}
return ok;
}
static void decode_callback(int what) {
if (what == 0 && !kParams.done) {
int duration = 0;
if (kParams.mux != NULL) {
if (!Decode(kParams.frame_num, &duration)) {
kParams.decoding_error = 1;
kParams.done = 1;
} else {
++kParams.frame_num;
if (kParams.frame_num > kParams.frame_max) {
kParams.frame_num = 1;
--kParams.loop_count;
kParams.done = (kParams.loop_count == 0);
}
}
}
glutPostRedisplay();
glutTimerFunc(duration, decode_callback, what);
}
}
//------------------------------------------------------------------------------
// Main
static void Help(void) {
printf("Usage: vwebp in_file [options]\n\n"
"Decodes the WebP image file and visualize it using OpenGL\n"
"Options are:\n"
" -version .... print version number and exit.\n"
" -nofancy ..... don't use the fancy YUV420 upscaler.\n"
" -nofilter .... disable in-loop filtering.\n"
" -mt .......... use multi-threading\n"
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
" -scale <w> <h> .......... scale the output (*after* any cropping)\n"
" -h ....... this help message.\n"
);
}
int main(int argc, char *argv[]) {
WebPDecoderConfig config;
WebPMuxError mux_err;
int c;
if (!WebPInitDecoderConfig(&config)) {
fprintf(stderr, "Library version mismatch!\n");
return -1;
}
kParams.config = &config;
for (c = 1; c < argc; ++c) {
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help();
return 0;
} else if (!strcmp(argv[c], "-nofancy")) {
config.options.no_fancy_upsampling = 1;
} else if (!strcmp(argv[c], "-nofilter")) {
config.options.bypass_filtering = 1;
} else if (!strcmp(argv[c], "-version")) {
const int version = WebPGetDecoderVersion();
printf("%d.%d.%d\n",
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
return 0;
} else if (!strcmp(argv[c], "-mt")) {
config.options.use_threads = 1;
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
config.options.use_cropping = 1;
config.options.crop_left = strtol(argv[++c], NULL, 0);
config.options.crop_top = strtol(argv[++c], NULL, 0);
config.options.crop_width = strtol(argv[++c], NULL, 0);
config.options.crop_height = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-scale") && c < argc - 2) {
config.options.use_scaling = 1;
config.options.scaled_width = strtol(argv[++c], NULL, 0);
config.options.scaled_height = strtol(argv[++c], NULL, 0);
} else if (argv[c][0] == '-') {
printf("Unknown option '%s'\n", argv[c]);
Help();
return -1;
} else {
kParams.file_name = argv[c];
}
}
if (kParams.file_name == NULL) {
printf("missing input file!!\n");
Help();
return 0;
}
if (!ExUtilReadFile(kParams.file_name,
&kParams.data.bytes_, &kParams.data.size_)) {
goto Error;
}
kParams.mux = WebPMuxCreate(&kParams.data, 0);
if (kParams.mux == NULL) {
fprintf(stderr, "Could not create demuxing object!\n");
goto Error;
}
mux_err = WebPMuxGetFeatures(kParams.mux, &kParams.flags);
if (mux_err != WEBP_MUX_OK) {
goto Error;
}
if (kParams.flags & TILE_FLAG) {
fprintf(stderr, "Tiling is not supported for now!\n");
goto Error;
}
kParams.has_animation = !!(kParams.flags & ANIMATION_FLAG);
if (kParams.has_animation) {
mux_err = WebPMuxGetLoopCount(kParams.mux, &kParams.loop_count);
if (mux_err != WEBP_MUX_OK && mux_err != WEBP_MUX_NOT_FOUND) {
goto Error;
}
mux_err = WebPMuxNumChunks(kParams.mux, WEBP_CHUNK_IMAGE,
&kParams.frame_max);
if (mux_err != WEBP_MUX_OK) {
goto Error;
}
printf("VP8X: Found %d images in file (loop count = %d)\n",
kParams.frame_max, kParams.loop_count);
}
// Decode first frame
{
int duration;
if (!Decode(1, &duration)) goto Error;
}
// Start display (and timer)
glutInit(&argc, argv);
#ifdef FREEGLUT
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
#endif
StartDisplay(kParams.pic);
if (kParams.has_animation) glutTimerFunc(0, decode_callback, 0);
glutMainLoop();
// Should only be reached when using FREEGLUT:
ClearParams();
return 0;
Error:
ClearParams();
return -1;
}
//------------------------------------------------------------------------------

942
examples/webpmux.c Normal file
View File

@ -0,0 +1,942 @@
// Copyright 2011 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Simple command-line to create a WebP container file and to extract or strip
// relevant data from the container file.
//
// Compile with: gcc -o webpmux webpmux.c -lwebpmux -lwebp
//
//
// Authors: Vikas (vikaas.arora@gmail.com),
// Urvang (urvang@google.com)
/* Usage examples:
Create container WebP file:
webpmux -tile tile_1.webp +0+0 \
-tile tile_2.webp +960+0 \
-tile tile_3.webp +0+576 \
-tile tile_4.webp +960+576 \
-o out_tile_container.webp
webpmux -frame anim_1.webp +0+0+0 \
-frame anim_2.webp +25+25+100 \
-frame anim_3.webp +50+50+100 \
-frame anim_4.webp +0+0+100 \
-loop 10 \
-o out_animation_container.webp
webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
Extract relevant data from WebP container file:
webpmux -get tile n in.webp -o out_tile.webp
webpmux -get frame n in.webp -o out_frame.webp
webpmux -get icc in.webp -o image_profile.icc
webpmux -get xmp in.webp -o image_metadata.xmp
Strip data from WebP Container file:
webpmux -strip icc in.webp -o out.webp
webpmux -strip xmp in.webp -o out.webp
Misc:
webpmux -info in.webp
webpmux [ -h | -help ]
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "webp/mux.h"
#include "./example_util.h"
//------------------------------------------------------------------------------
// Config object to parse command-line arguments.
typedef enum {
NIL_ACTION = 0,
ACTION_GET,
ACTION_SET,
ACTION_STRIP,
ACTION_INFO,
ACTION_HELP
} ActionType;
typedef enum {
NIL_SUBTYPE = 0,
SUBTYPE_FRM,
SUBTYPE_LOOP
} FeatureSubType;
typedef struct {
FeatureSubType subtype_;
const char* filename_;
const char* params_;
} FeatureArg;
typedef enum {
NIL_FEATURE = 0,
FEATURE_XMP,
FEATURE_ICCP,
FEATURE_FRM,
FEATURE_TILE
} FeatureType;
typedef struct {
FeatureType type_;
FeatureArg* args_;
int arg_count_;
} Feature;
typedef struct {
ActionType action_type_;
const char* input_;
const char* output_;
Feature feature_;
} WebPMuxConfig;
//------------------------------------------------------------------------------
// Helper functions.
static int CountOccurrences(const char* arglist[], int list_length,
const char* arg) {
int i;
int num_occurences = 0;
for (i = 0; i < list_length; ++i) {
if (!strcmp(arglist[i], arg)) {
++num_occurences;
}
}
return num_occurences;
}
static const char* const kErrorMessages[] = {
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
};
static const char* ErrorString(WebPMuxError err) {
assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
return kErrorMessages[-err];
}
static int IsNotCompatible(int count1, int count2) {
return (count1 > 0) != (count2 > 0);
}
#define RETURN_IF_ERROR(ERR_MSG) \
if (err != WEBP_MUX_OK) { \
fprintf(stderr, ERR_MSG); \
return err; \
}
#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
if (err != WEBP_MUX_OK) { \
fprintf(stderr, ERR_MSG, FORMAT_STR); \
return err; \
}
#define ERROR_GOTO1(ERR_MSG, LABEL) \
do { \
fprintf(stderr, ERR_MSG); \
ok = 0; \
goto LABEL; \
} while (0)
#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
do { \
fprintf(stderr, ERR_MSG, FORMAT_STR); \
ok = 0; \
goto LABEL; \
} while (0)
#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
do { \
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
ok = 0; \
goto LABEL; \
} while (0)
static WebPMuxError DisplayInfo(const WebPMux* mux) {
uint32_t flag;
WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
RETURN_IF_ERROR("Failed to retrieve features\n");
if (flag == 0) {
fprintf(stderr, "No features present.\n");
return err;
}
// Print the features present.
printf("Features present:");
if (flag & ANIMATION_FLAG) printf(" animation");
if (flag & TILE_FLAG) printf(" tiling");
if (flag & ICCP_FLAG) printf(" icc profile");
if (flag & META_FLAG) printf(" metadata");
if (flag & ALPHA_FLAG) printf(" transparency");
printf("\n");
if (flag & ANIMATION_FLAG) {
int nFrames;
int loop_count;
err = WebPMuxGetLoopCount(mux, &loop_count);
RETURN_IF_ERROR("Failed to retrieve loop count\n");
printf("Loop Count : %d\n", loop_count);
err = WebPMuxNumChunks(mux, WEBP_CHUNK_FRAME, &nFrames);
RETURN_IF_ERROR("Failed to retrieve number of frames\n");
printf("Number of frames: %d\n", nFrames);
if (nFrames > 0) {
int i;
printf("No.: x_offset y_offset duration image_size");
printf("\n");
for (i = 1; i <= nFrames; i++) {
int x_offset, y_offset, duration;
WebPData bitstream;
err = WebPMuxGetFrame(mux, i, &bitstream,
&x_offset, &y_offset, &duration);
RETURN_IF_ERROR2("Failed to retrieve frame#%d\n", i);
printf("%3d: %8d %8d %8d %10zu",
i, x_offset, y_offset, duration, bitstream.size_);
printf("\n");
}
}
}
if (flag & TILE_FLAG) {
int nTiles;
err = WebPMuxNumChunks(mux, WEBP_CHUNK_TILE, &nTiles);
RETURN_IF_ERROR("Failed to retrieve number of tiles\n");
printf("Number of tiles: %d\n", nTiles);
if (nTiles > 0) {
int i;
printf("No.: x_offset y_offset image_size");
printf("\n");
for (i = 1; i <= nTiles; i++) {
int x_offset, y_offset;
WebPData bitstream;
err = WebPMuxGetTile(mux, i, &bitstream, &x_offset, &y_offset);
RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i);
printf("%3d: %8d %8d %10zu", i, x_offset, y_offset, bitstream.size_);
printf("\n");
}
}
}
if (flag & ICCP_FLAG) {
WebPData icc_profile;
err = WebPMuxGetColorProfile(mux, &icc_profile);
RETURN_IF_ERROR("Failed to retrieve the color profile\n");
printf("Size of the color profile data: %zu\n", icc_profile.size_);
}
if (flag & META_FLAG) {
WebPData metadata;
err = WebPMuxGetMetadata(mux, &metadata);
RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
printf("Size of the XMP metadata: %zu\n", metadata.size_);
}
if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) {
WebPData bitstream;
err = WebPMuxGetImage(mux, &bitstream);
RETURN_IF_ERROR("Failed to retrieve the image\n");
printf("Size of the image (with alpha): %zu\n", bitstream.size_);
}
return WEBP_MUX_OK;
}
static void PrintHelp(void) {
printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -tile TILE_OPTIONS [-tile...] -o OUTPUT\n");
printf(" webpmux -frame FRAME_OPTIONS [-frame...]");
printf(" -loop LOOP_COUNT -o OUTPUT\n");
printf(" webpmux -info INPUT\n");
printf(" webpmux [-h|-help]\n");
printf("\n");
printf("GET_OPTIONS:\n");
printf(" Extract relevant data.\n");
printf(" icc Get ICCP Color profile.\n");
printf(" xmp Get XMP metadata.\n");
printf(" tile n Get nth tile.\n");
printf(" frame n Get nth frame.\n");
printf("\n");
printf("SET_OPTIONS:\n");
printf(" Set color profile/metadata.\n");
printf(" icc Set ICC Color profile.\n");
printf(" xmp Set XMP metadata.\n");
printf("\n");
printf("STRIP_OPTIONS:\n");
printf(" Strip color profile/metadata.\n");
printf(" icc Strip ICCP color profile.\n");
printf(" xmp Strip XMP metadata.\n");
printf("\n");
printf("TILE_OPTIONS(i):\n");
printf(" Create tiled image.\n");
printf(" file_i +xi+yi\n");
printf(" where: 'file_i' is the i'th tile (webp format),\n");
printf(" 'xi','yi' specify the image offset for this tile.\n");
printf("\n");
printf("FRAME_OPTIONS(i):\n");
printf(" Create animation.\n");
printf(" file_i +xi+yi+di\n");
printf(" where: 'file_i' is the i'th animation frame (webp format),\n");
printf(" 'xi','yi' specify the image offset for this frame.\n");
printf(" 'di' is the pause duration before next frame.\n");
printf("\nINPUT & OUTPUT are in webp format.\n");
}
static int ReadFileToWebPData(const char* const filename,
WebPData* const webp_data) {
const uint8_t* data;
size_t size;
if (!ExUtilReadFile(filename, &data, &size)) return 0;
webp_data->bytes_ = data;
webp_data->size_ = size;
return 1;
}
static int CreateMux(const char* const filename, WebPMux** mux) {
WebPData bitstream;
assert(mux != NULL);
if (!ReadFileToWebPData(filename, &bitstream)) return 0;
*mux = WebPMuxCreate(&bitstream, 1);
free((void*)bitstream.bytes_);
if (*mux != NULL) return 1;
fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
return 0;
}
static int WriteData(const char* filename, const WebPData* const webpdata) {
int ok = 0;
FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
if (!fout) {
fprintf(stderr, "Error opening output WebP file %s!\n", filename);
return 0;
}
if (fwrite(webpdata->bytes_, webpdata->size_, 1, fout) != 1) {
fprintf(stderr, "Error writing file %s!\n", filename);
} else {
fprintf(stderr, "Saved file %s (%zu bytes)\n", filename, webpdata->size_);
ok = 1;
}
if (fout != stdout) fclose(fout);
return ok;
}
static int WriteWebP(WebPMux* const mux, const char* filename) {
int ok;
WebPData webp_data;
const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
if (err != WEBP_MUX_OK) {
fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
return 0;
}
ok = WriteData(filename, &webp_data);
WebPDataClear(&webp_data);
return ok;
}
static int ParseFrameArgs(const char* args, int* const x_offset,
int* const y_offset, int* const duration) {
return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3);
}
static int ParseTileArgs(const char* args,
int* const x_offset, int* const y_offset) {
return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2);
}
//------------------------------------------------------------------------------
// Clean-up.
static void DeleteConfig(WebPMuxConfig* config) {
if (config != NULL) {
free(config->feature_.args_);
free(config);
}
}
//------------------------------------------------------------------------------
// Parsing.
// Basic syntactic checks on the command-line arguments.
// Returns 1 on valid, 0 otherwise.
// Also fills up num_feature_args to be number of feature arguments given.
// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
static int ValidateCommandLine(int argc, const char* argv[],
int* num_feature_args) {
int num_frame_args;
int num_tile_args;
int num_loop_args;
int ok = 1;
assert(num_feature_args != NULL);
*num_feature_args = 0;
// Simple checks.
if (CountOccurrences(argv, argc, "-get") > 1) {
ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
}
if (CountOccurrences(argv, argc, "-set") > 1) {
ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
}
if (CountOccurrences(argv, argc, "-strip") > 1) {
ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
}
if (CountOccurrences(argv, argc, "-info") > 1) {
ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
}
if (CountOccurrences(argv, argc, "-o") > 1) {
ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
}
// Compound checks.
num_frame_args = CountOccurrences(argv, argc, "-frame");
num_tile_args = CountOccurrences(argv, argc, "-tile");
num_loop_args = CountOccurrences(argv, argc, "-loop");
if (num_loop_args > 1) {
ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
}
if (IsNotCompatible(num_frame_args, num_loop_args)) {
ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
ErrValidate);
}
if (num_frame_args > 0 && num_tile_args > 0) {
ERROR_GOTO1("ERROR: Only one of frames & tiles can be specified at a time."
"\n", ErrValidate);
}
assert(ok == 1);
if (num_frame_args == 0 && num_tile_args == 0) {
// Single argument ('set' action for XMP or ICCP, OR a 'get' action).
*num_feature_args = 1;
} else {
// Multiple arguments ('set' action for animation or tiling).
if (num_frame_args > 0) {
*num_feature_args = num_frame_args + num_loop_args;
} else {
*num_feature_args = num_tile_args;
}
}
ErrValidate:
return ok;
}
#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
if (argc < i + (NUM)) { \
fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
goto LABEL; \
}
#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
if (argc != i + (NUM)) { \
fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
goto LABEL; \
}
// Parses command-line arguments to fill up config object. Also performs some
// semantic checks.
static int ParseCommandLine(int argc, const char* argv[],
WebPMuxConfig* config) {
int i = 0;
int feature_arg_index = 0;
int ok = 1;
while (i < argc) {
Feature* const feature = &config->feature_;
FeatureArg* const arg = &feature->args_[feature_arg_index];
if (argv[i][0] == '-') { // One of the action types or output.
if (!strcmp(argv[i], "-set")) {
if (ACTION_IS_NIL) {
config->action_type_ = ACTION_SET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
++i;
} else if (!strcmp(argv[i], "-get")) {
if (ACTION_IS_NIL) {
config->action_type_ = ACTION_GET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
++i;
} else if (!strcmp(argv[i], "-strip")) {
if (ACTION_IS_NIL) {
config->action_type_ = ACTION_STRIP;
feature->arg_count_ = 0;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
++i;
} else if (!strcmp(argv[i], "-frame")) {
CHECK_NUM_ARGS_LESS(3, ErrParse);
if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
config->action_type_ = ACTION_SET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
feature->type_ = FEATURE_FRM;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
arg->subtype_ = SUBTYPE_FRM;
arg->filename_ = argv[i + 1];
arg->params_ = argv[i + 2];
++feature_arg_index;
i += 3;
} else if (!strcmp(argv[i], "-loop")) {
CHECK_NUM_ARGS_LESS(2, ErrParse);
if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
config->action_type_ = ACTION_SET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
feature->type_ = FEATURE_FRM;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
arg->subtype_ = SUBTYPE_LOOP;
arg->params_ = argv[i + 1];
++feature_arg_index;
i += 2;
} else if (!strcmp(argv[i], "-tile")) {
CHECK_NUM_ARGS_LESS(3, ErrParse);
if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
config->action_type_ = ACTION_SET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_TILE) {
feature->type_ = FEATURE_TILE;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
arg->filename_ = argv[i + 1];
arg->params_ = argv[i + 2];
++feature_arg_index;
i += 3;
} else if (!strcmp(argv[i], "-o")) {
CHECK_NUM_ARGS_LESS(2, ErrParse);
config->output_ = argv[i + 1];
i += 2;
} else if (!strcmp(argv[i], "-info")) {
CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
if (config->action_type_ != NIL_ACTION) {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
} else {
config->action_type_ = ACTION_INFO;
feature->arg_count_ = 0;
config->input_ = argv[i + 1];
}
i += 2;
} else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
PrintHelp();
DeleteConfig(config);
exit(0);
} else {
ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
}
} else { // One of the feature types or input.
if (ACTION_IS_NIL) {
ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
ErrParse);
}
if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "xmp")) {
if (FEATURETYPE_IS_NIL) {
feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
FEATURE_XMP;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
if (config->action_type_ == ACTION_SET) {
CHECK_NUM_ARGS_LESS(2, ErrParse);
arg->filename_ = argv[i + 1];
++feature_arg_index;
i += 2;
} else {
++i;
}
} else if ((!strcmp(argv[i], "frame") ||
!strcmp(argv[i], "tile")) &&
(config->action_type_ == ACTION_GET)) {
CHECK_NUM_ARGS_LESS(2, ErrParse);
feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_FRM :
FEATURE_TILE;
arg->params_ = argv[i + 1];
++feature_arg_index;
i += 2;
} else { // Assume input file.
if (config->input_ == NULL) {
config->input_ = argv[i];
} else {
ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
argv[i], ErrParse);
}
++i;
}
}
}
ErrParse:
return ok;
}
// Additional checks after config is filled.
static int ValidateConfig(WebPMuxConfig* config) {
int ok = 1;
Feature* const feature = &config->feature_;
// Action.
if (ACTION_IS_NIL) {
ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
}
// Feature type.
if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
}
// Input file.
if (config->input_ == NULL) {
if (config->action_type_ != ACTION_SET) {
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
} else if (feature->type_ != FEATURE_FRM &&
feature->type_ != FEATURE_TILE) {
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
}
}
// Output file.
if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
}
ErrValidate2:
return ok;
}
// Create config object from command-line arguments.
static int InitializeConfig(int argc, const char* argv[],
WebPMuxConfig** config) {
int num_feature_args = 0;
int ok = 1;
assert(config != NULL);
*config = NULL;
// Validate command-line arguments.
if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
}
// Allocate memory.
*config = (WebPMuxConfig*)calloc(1, sizeof(**config));
if (*config == NULL) {
ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
}
(*config)->feature_.arg_count_ = num_feature_args;
(*config)->feature_.args_ =
(FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
if ((*config)->feature_.args_ == NULL) {
ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
}
// Parse command-line.
if (!ParseCommandLine(argc, argv, *config) ||
!ValidateConfig(*config)) {
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
}
Err1:
return ok;
}
#undef ACTION_IS_NIL
#undef FEATURETYPE_IS_NIL
#undef CHECK_NUM_ARGS_LESS
#undef CHECK_NUM_ARGS_MORE
//------------------------------------------------------------------------------
// Processing.
static int GetFrameTile(const WebPMux* mux,
const WebPMuxConfig* config, int isFrame) {
WebPData bitstream;
int x_offset = 0;
int y_offset = 0;
int duration = 0;
WebPMuxError err = WEBP_MUX_OK;
WebPMux* mux_single = NULL;
long num = 0;
int ok = 1;
num = strtol(config->feature_.args_[0].params_, NULL, 10);
if (num < 0) {
ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet);
}
if (isFrame) {
err = WebPMuxGetFrame(mux, num, &bitstream,
&x_offset, &y_offset, &duration);
if (err != WEBP_MUX_OK) {
ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
ErrorString(err), num, ErrGet);
}
} else {
err = WebPMuxGetTile(mux, num, &bitstream, &x_offset, &y_offset);
if (err != WEBP_MUX_OK) {
ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
ErrorString(err), num, ErrGet);
}
}
mux_single = WebPMuxNew();
if (mux_single == NULL) {
err = WEBP_MUX_MEMORY_ERROR;
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
ErrorString(err), ErrGet);
}
err = WebPMuxSetImage(mux_single, &bitstream, 1);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
ErrorString(err), ErrGet);
}
ok = WriteWebP(mux_single, config->output_);
ErrGet:
WebPMuxDelete(mux_single);
return ok;
}
// Read and process config.
static int Process(const WebPMuxConfig* config) {
WebPMux* mux = NULL;
WebPData webpdata;
WebPData metadata, color_profile;
int x_offset = 0;
int y_offset = 0;
WebPMuxError err = WEBP_MUX_OK;
int index = 0;
int ok = 1;
const Feature* const feature = &config->feature_;
switch (config->action_type_) {
case ACTION_GET:
ok = CreateMux(config->input_, &mux);
if (!ok) goto Err2;
switch (feature->type_) {
case FEATURE_FRM:
ok = GetFrameTile(mux, config, 1);
break;
case FEATURE_TILE:
ok = GetFrameTile(mux, config, 0);
break;
case FEATURE_ICCP:
err = WebPMuxGetColorProfile(mux, &webpdata);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("ERROR (%s): Could not get color profile.\n",
ErrorString(err), Err2);
}
ok = WriteData(config->output_, &webpdata);
break;
case FEATURE_XMP:
err = WebPMuxGetMetadata(mux, &webpdata);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("ERROR (%s): Could not get XMP metadata.\n",
ErrorString(err), Err2);
}
ok = WriteData(config->output_, &webpdata);
break;
default:
ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
break;
}
break;
case ACTION_SET:
switch (feature->type_) {
case FEATURE_FRM:
mux = WebPMuxNew();
if (mux == NULL) {
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
}
for (index = 0; index < feature->arg_count_; ++index) {
if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
const long num = strtol(feature->args_[index].params_, NULL, 10);
if (num < 0) {
ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
}
err = WebPMuxSetLoopCount(mux, num);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("ERROR (%s): Could not set loop count.\n",
ErrorString(err), Err2);
}
} else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
int duration;
ok = ReadFileToWebPData(feature->args_[index].filename_,
&webpdata);
if (!ok) goto Err2;
ok = ParseFrameArgs(feature->args_[index].params_,
&x_offset, &y_offset, &duration);
if (!ok) {
WebPDataClear(&webpdata);
ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
}
err = WebPMuxPushFrame(mux, &webpdata, x_offset, y_offset,
duration, 1);
WebPDataClear(&webpdata);
if (err != WEBP_MUX_OK) {
ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d.\n",
ErrorString(err), index, Err2);
}
} else {
ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
}
}
break;
case FEATURE_TILE:
mux = WebPMuxNew();
if (mux == NULL) {
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
}
for (index = 0; index < feature->arg_count_; ++index) {
ok = ReadFileToWebPData(feature->args_[index].filename_, &webpdata);
if (!ok) goto Err2;
ok = ParseTileArgs(feature->args_[index].params_, &x_offset,
&y_offset);
if (!ok) {
WebPDataClear(&webpdata);
ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2);
}
err = WebPMuxPushTile(mux, &webpdata, x_offset, y_offset, 1);
WebPDataClear(&webpdata);
if (err != WEBP_MUX_OK) {
ERROR_GOTO3("ERROR (%s): Could not add a tile at index %d.\n",
ErrorString(err), index, Err2);
}
}
break;
case FEATURE_ICCP:
ok = CreateMux(config->input_, &mux);
if (!ok) goto Err2;
ok = ReadFileToWebPData(feature->args_[0].filename_, &color_profile);
if (!ok) goto Err2;
err = WebPMuxSetColorProfile(mux, &color_profile, 1);
free((void*)color_profile.bytes_);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("ERROR (%s): Could not set color profile.\n",
ErrorString(err), Err2);
}
break;
case FEATURE_XMP:
ok = CreateMux(config->input_, &mux);
if (!ok) goto Err2;
ok = ReadFileToWebPData(feature->args_[0].filename_, &metadata);
if (!ok) goto Err2;
err = WebPMuxSetMetadata(mux, &metadata, 1);
free((void*)metadata.bytes_);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("ERROR (%s): Could not set XMP metadata.\n",
ErrorString(err), Err2);
}
break;
default:
ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
break;
}
ok = WriteWebP(mux, config->output_);
break;
case ACTION_STRIP:
ok = CreateMux(config->input_, &mux);
if (!ok) goto Err2;
switch (feature->type_) {
case FEATURE_ICCP:
err = WebPMuxDeleteColorProfile(mux);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("ERROR (%s): Could not delete color profile.\n",
ErrorString(err), Err2);
}
break;
case FEATURE_XMP:
err = WebPMuxDeleteMetadata(mux);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("ERROR (%s): Could not delete XMP metadata.\n",
ErrorString(err), Err2);
}
break;
default:
ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
break;
}
ok = WriteWebP(mux, config->output_);
break;
case ACTION_INFO:
ok = CreateMux(config->input_, &mux);
if (!ok) goto Err2;
ok = (DisplayInfo(mux) == WEBP_MUX_OK);
break;
default:
assert(0); // Invalid action.
break;
}
Err2:
WebPMuxDelete(mux);
return ok;
}
//------------------------------------------------------------------------------
// Main.
int main(int argc, const char* argv[]) {
WebPMuxConfig* config;
int ok = InitializeConfig(argc - 1, argv + 1, &config);
if (ok) {
ok = Process(config);
} else {
PrintHelp();
}
DeleteConfig(config);
return !ok;
}
//------------------------------------------------------------------------------

309
m4/ax_pthread.m4 Normal file
View File

@ -0,0 +1,309 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro figures out how to build C programs using POSIX threads. It
# sets the PTHREAD_LIBS output variable to the threads library and linker
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
# Also sets PTHREAD_CC to any special C compiler that is needed for
# multi-threaded programs (defaults to the value of CC otherwise). (This
# is necessary on AIX to use the special cc_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also link it with them as well. e.g. you should link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threads programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
#
# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
# PTHREAD_CFLAGS.
#
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
# is not found. If ACTION-IF-FOUND is not specified, the default action
# will define HAVE_PTHREAD.
#
# Please let the authors know if this macro fails on any platform, or if
# you have any other suggestions or comments. This macro was based on work
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
# grateful for the helpful feedback of numerous users.
#
# Updated for Autoconf 2.68 by Daniel Richard G.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 18
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_LANG_PUSH([C])
ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on True64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
AC_MSG_RESULT($ax_pthread_ok)
if test x"$ax_pthread_ok" = xno; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items starting with a "-" are
# C compiler flags, and other items are library names, except for "none"
# which indicates that we try without any flags at all, and "pthread-config"
# which is a program returning the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
# -pthreads: Solaris/gcc
# -mthreads: Mingw32/gcc, Lynx/gcc
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# ... -mt is also the pthreads flag for HP/aCC
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
case ${host_os} in
solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
;;
darwin*)
ax_pthread_flags="-pthread $ax_pthread_flags"
;;
esac
if test x"$ax_pthread_ok" = xno; then
for flag in $ax_pthread_flags; do
case $flag in
none)
AC_MSG_CHECKING([whether pthreads work without any flags])
;;
-*)
AC_MSG_CHECKING([whether pthreads work with $flag])
PTHREAD_CFLAGS="$flag"
;;
pthread-config)
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
if test x"$ax_pthread_config" = xno; then continue; fi
PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$flag])
PTHREAD_LIBS="-l$flag"
;;
esac
save_LIBS="$LIBS"
save_CFLAGS="$CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
static void routine(void *a) { a = 0; }
static void *start_routine(void *a) { return a; }],
[pthread_t th; pthread_attr_t attr;
pthread_create(&th, 0, start_routine, 0);
pthread_join(th, 0);
pthread_attr_init(&attr);
pthread_cleanup_push(routine, 0);
pthread_cleanup_pop(0) /* ; */])],
[ax_pthread_ok=yes],
[])
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
AC_MSG_RESULT($ax_pthread_ok)
if test "x$ax_pthread_ok" = xyes; then
break;
fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$ax_pthread_ok" = xyes; then
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
AC_MSG_CHECKING([for joinable pthread attribute])
attr_name=unknown
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
[int attr = $attr; return attr /* ; */])],
[attr_name=$attr; break],
[])
done
AC_MSG_RESULT($attr_name)
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
[Define to necessary symbol if this constant
uses a non-standard name on your system.])
fi
AC_MSG_CHECKING([if more special flags are required for pthreads])
flag=no
case ${host_os} in
aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
osf* | hpux*) flag="-D_REENTRANT";;
solaris*)
if test "$GCC" = "yes"; then
flag="-D_REENTRANT"
else
flag="-mt -D_REENTRANT"
fi
;;
esac
AC_MSG_RESULT(${flag})
if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
ax_cv_PTHREAD_PRIO_INHERIT, [
AC_LINK_IFELSE([
AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
[ax_cv_PTHREAD_PRIO_INHERIT=no])
])
AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
# More AIX lossage: must compile with xlc_r or cc_r
if test x"$GCC" != xyes; then
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
else
PTHREAD_CC=$CC
fi
else
PTHREAD_CC="$CC"
fi
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_CC)
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test x"$ax_pthread_ok" = xyes; then
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
:
else
ax_pthread_ok=no
$2
fi
AC_LANG_POP
])dnl AX_PTHREAD

View File

@ -2,7 +2,7 @@
# system, for simple local building of the libraries and tools. # system, for simple local building of the libraries and tools.
# It will not install the libraries system-wide, but just create the 'cwebp' # It will not install the libraries system-wide, but just create the 'cwebp'
# and 'dwebp' tools in the examples/ directory, along with the static # and 'dwebp' tools in the examples/ directory, along with the static
# library 'src/libwebp.a'. # libraries 'src/libwebp.a' and 'src/mux/libwebpmux.a'.
# #
# To build the library and examples, use: # To build the library and examples, use:
# make -f makefile.unix # make -f makefile.unix
@ -12,21 +12,30 @@
# These flag assume you have libpng and libjpeg installed. If not, either # These flag assume you have libpng and libjpeg installed. If not, either
# follow below install instructions or just comment out the next lines. # follow below install instructions or just comment out the next lines.
EXTRA_FLAGS= -DWEBP_HAVE_PNG -DWEBP_HAVE_JPEG EXTRA_FLAGS= -DWEBP_HAVE_PNG -DWEBP_HAVE_JPEG -DWEBP_HAVE_TIFF
EXTRA_LIBS= -lpng -ljpeg -lz EXTRA_LIBS= -lpng -ltiff -ljpeg -lz
ifeq ($(strip $(shell uname)), Darwin) ifeq ($(strip $(shell uname)), Darwin)
# Work around a problem linking tables marked as common symbols,
# cf., src/enc/yuv.[hc]
# Failure observed with: gcc 4.2.1 and 4.0.1.
EXTRA_FLAGS += -fno-common
EXTRA_FLAGS += -I/opt/local/include EXTRA_FLAGS += -I/opt/local/include
EXTRA_LIBS += -L/opt/local/lib EXTRA_LIBS += -L/opt/local/lib
GL_LIBS = -framework GLUT -framework OpenGL
else
GL_LIBS = -lglut -lGL
endif endif
# To install libraries on Mac OS X: # To install libraries on Mac OS X:
# 1. Install MacPorts (http://www.macports.org/install.php) # 1. Install MacPorts (http://www.macports.org/install.php)
# 2. Run "sudo port install jpeg" # 2. Run "sudo port install jpeg"
# 3. Run "sudo port install libpng" # 3. Run "sudo port install libpng"
# 4. Run "sudo port install tiff"
# To install libraries on Linux: # To install libraries on Linux:
# 1. Run "sudo apt-get install libjpeg62-dev" # 1. Run "sudo apt-get install libjpeg62-dev"
# 2. Run "sudo apt-get install libpng12-dev" # 2. Run "sudo apt-get install libpng12-dev"
# 3. Run "sudo apt-get install libtiff4-dev"
# Uncomment for build for 32bit platform # Uncomment for build for 32bit platform
# Alternatively, you can just use the command # Alternatively, you can just use the command
@ -51,72 +60,170 @@ EXTRA_FLAGS += -Wdeclaration-after-statement
AR = ar AR = ar
ARFLAGS = r ARFLAGS = r
CC = gcc -Isrc/ -Iexamples/ -Wall CC = gcc
CPPFLAGS = -Isrc/ -Wall
CFLAGS = -O3 -DNDEBUG $(EXTRA_FLAGS) CFLAGS = -O3 -DNDEBUG $(EXTRA_FLAGS)
INSTALL = install INSTALL = install
GROFF = /usr/bin/groff
COL = /usr/bin/col
LDFLAGS = $(EXTRA_LIBS) -lm LDFLAGS = $(EXTRA_LIBS) -lm
DEC_OBJS = src/dec/frame.o src/dec/webp.o src/dec/quant.o src/dec/tree.o \ DEC_OBJS = \
src/dec/vp8.o src/dec/idec.o src/dec/alpha.o src/dec/layer.o \ src/dec/alpha.o \
src/dec/io.o src/dec/buffer.o src/dec/buffer.o \
ENC_OBJS = src/enc/webpenc.o src/enc/syntax.o \ src/dec/frame.o \
src/enc/alpha.o src/enc/layer.o \ src/dec/idec.o \
src/enc/tree.o src/enc/config.o src/enc/frame.o \ src/dec/io.o \
src/enc/quant.o src/enc/iterator.o src/enc/analysis.o \ src/dec/layer.o \
src/enc/cost.o src/enc/picture.o src/enc/filter.o src/dec/quant.o \
DSP_OBJS = src/dsp/cpu.o src/dsp/enc.o \ src/dec/tree.o \
src/dsp/enc_sse2.o src/dsp/dec.o src/dsp/dec_sse2.o \ src/dec/vp8.o \
src/dsp/dec_neon.o src/dsp/upsampling.o src/dsp/upsampling_sse2.o \ src/dec/vp8l.o \
src/dsp/yuv.o src/dec/webp.o \
UTILS_OBJS = src/utils/bit_reader.o src/utils/bit_writer.o src/utils/thread.o
OBJS = $(DEC_OBJS) $(ENC_OBJS) $(DSP_OBJS) $(UTILS_OBJS) DSP_OBJS = \
src/dsp/cpu.o \
src/dsp/dec.o \
src/dsp/dec_neon.o \
src/dsp/dec_sse2.o \
src/dsp/enc.o \
src/dsp/enc_sse2.o \
src/dsp/lossless.o \
src/dsp/upsampling.o \
src/dsp/upsampling_sse2.o \
src/dsp/yuv.o \
HDRS = src/webp/encode.h src/enc/vp8enci.h src/enc/cost.h \ ENC_OBJS = \
src/dec/vp8i.h \ src/enc/alpha.o \
src/dsp/yuv.h src/dsp/dsp.h \ src/enc/analysis.o \
src/utils/bit_writer.h src/utils/bit_reader.h src/utils/thread.h src/enc/backward_references.o \
src/enc/config.o \
src/enc/cost.o \
src/enc/filter.o \
src/enc/frame.o \
src/enc/histogram.o \
src/enc/iterator.o \
src/enc/layer.o \
src/enc/picture.o \
src/enc/quant.o \
src/enc/syntax.o \
src/enc/tree.o \
src/enc/vp8l.o \
src/enc/webpenc.o \
OUTPUT = examples/cwebp examples/dwebp src/libwebp.a EX_UTIL_OBJS = \
examples/example_util.o \
all:ex MUX_OBJS = \
src/mux/demux.o \
src/mux/muxedit.o \
src/mux/muxinternal.o \
src/mux/muxread.o \
UTILS_OBJS = \
src/utils/bit_reader.o \
src/utils/bit_writer.o \
src/utils/color_cache.o \
src/utils/filters.o \
src/utils/huffman.o \
src/utils/huffman_encode.o \
src/utils/quant_levels.o \
src/utils/rescaler.o \
src/utils/thread.o \
src/utils/utils.o \
LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS)
LIBWEBPMUX_OBJS = $(MUX_OBJS)
HDRS_INSTALLED = \
src/webp/decode.h \
src/webp/encode.h \
src/webp/types.h \
HDRS = \
src/dec/decode_vp8.h \
src/dec/vp8i.h \
src/dec/vp8li.h \
src/dec/webpi.h \
src/dsp/dsp.h \
src/dsp/lossless.h \
src/dsp/yuv.h \
src/enc/cost.h \
src/enc/vp8enci.h \
src/utils/bit_reader.h \
src/utils/bit_writer.h \
src/utils/color_cache.h \
src/utils/filters.h \
src/utils/huffman.h \
src/utils/huffman_encode.h \
src/utils/quant_levels.h \
src/utils/rescaler.h \
src/utils/thread.h \
src/webp/format_constants.h \
src/webp/mux.h \
$(HDRS_INSTALLED) \
OUT_LIBS = examples/libexample_util.a src/libwebp.a
OUT_EXAMPLES = examples/cwebp examples/dwebp
OUTPUT = $(OUT_LIBS) $(OUT_EXAMPLES)
ifeq ($(MAKECMDGOALS),clean)
OUTPUT += examples/vwebp examples/webpmux src/mux/libwebpmux.a
endif
all: ex
%.o: %.c $(HDRS) %.o: %.c $(HDRS)
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
src/libwebp.a: $(OBJS) examples/libexample_util.a: $(EX_UTIL_OBJS)
src/libwebp.a: $(LIBWEBP_OBJS)
src/mux/libwebpmux.a: $(LIBWEBPMUX_OBJS)
%.a:
$(AR) $(ARFLAGS) $@ $^ $(AR) $(ARFLAGS) $@ $^
ex: examples/cwebp examples/dwebp ex: $(OUT_EXAMPLES)
examples/cwebp: examples/cwebp.o src/libwebp.a examples/cwebp: examples/cwebp.o
examples/dwebp: examples/dwebp.o src/libwebp.a examples/dwebp: examples/dwebp.o
examples/cwebp examples/dwebp: examples/vwebp: examples/vwebp.o
examples/webpmux: examples/webpmux.o
examples/cwebp: src/libwebp.a
examples/dwebp: examples/libexample_util.a src/libwebp.a
examples/vwebp: examples/libexample_util.a src/mux/libwebpmux.a src/libwebp.a
examples/vwebp: EXTRA_LIBS += $(GL_LIBS)
examples/webpmux: examples/libexample_util.a src/mux/libwebpmux.a src/libwebp.a
$(OUT_EXAMPLES) examples/vwebp examples/webpmux:
$(CC) -o $@ $^ $(LDFLAGS) $(CC) -o $@ $^ $(LDFLAGS)
dist: DESTDIR := dist dist: DESTDIR := dist
dist: all dist: all
$(INSTALL) -m755 -d $(DESTDIR)/include/webp \ $(INSTALL) -m755 -d $(DESTDIR)/include/webp \
$(DESTDIR)/doc $(DESTDIR)/lib $(DESTDIR)/doc $(DESTDIR)/lib
$(INSTALL) -m755 -s examples/cwebp examples/dwebp $(DESTDIR) $(INSTALL) -m755 -s $(OUT_EXAMPLES) $(DESTDIR)
$(INSTALL) -m644 src/webp/*.h $(DESTDIR)/include/webp $(INSTALL) -m644 $(HDRS_INSTALLED) $(DESTDIR)/include/webp
$(INSTALL) -m644 src/libwebp.a $(DESTDIR)/lib $(INSTALL) -m644 src/libwebp.a $(DESTDIR)/lib
umask 022; \ umask 022; \
for m in man/[cd]webp.1; do \ for m in man/[cd]webp.1; do \
basenam=$$(basename $$m .1); \ basenam=$$(basename $$m .1); \
/usr/bin/groff -t -e -man -T utf8 $$m \ $(GROFF) -t -e -man -T utf8 $$m \
| col -bx >$(DESTDIR)/doc/$${basenam}.txt; \ | $(COL) -bx >$(DESTDIR)/doc/$${basenam}.txt; \
/usr/bin/groff -t -e -man -T html $$m \ $(GROFF) -t -e -man -T html $$m \
| col -bx >$(DESTDIR)/doc/$${basenam}.html; \ | $(COL) -bx >$(DESTDIR)/doc/$${basenam}.html; \
done done
clean: clean:
$(RM) ${OUTPUT} *~ \ $(RM) $(OUTPUT) *~ \
src/enc/*.o src/enc/*~ \ examples/*.o examples/*~ \
src/dec/*.o src/dec/*~ \ src/dec/*.o src/dec/*~ \
src/dsp/*.o src/dsp/*~ \ src/dsp/*.o src/dsp/*~ \
src/enc/*.o src/enc/*~ \
src/mux/*.o src/mux/*~ \
src/utils/*.o src/utils/*~ \ src/utils/*.o src/utils/*~ \
examples/*.o examples/*~ src/webp/*~ man/*~ doc/*~ swig/*~ \
superclean: clean superclean: clean
$(RM) -r .git *.log *.cache *~ $(RM) -r .git *.log *.cache *~

View File

@ -1,2 +1,5 @@
man_MANS = cwebp.1 dwebp.1 man_MANS = cwebp.1 dwebp.1
if WANT_MUX
man_MANS += webpmux.1
endif
EXTRA_DIST = $(man_MANS) EXTRA_DIST = $(man_MANS)

View File

@ -1,10 +1,10 @@
.\" Hey, EMACS: -*- nroff -*- .\" Hey, EMACS: -*- nroff -*-
.TH CWEBP 1 "September 19, 2011" .TH CWEBP 1 "July 19, 2012"
.SH NAME .SH NAME
cwebp \- compress an image file to a WebP file cwebp \- compress an image file to a WebP file
.SH SYNOPSIS .SH SYNOPSIS
.B cwebp .B cwebp
.RI [ options ] " input_file -o output_file.webp .RI [ options ] " input_file \-o output_file.webp
.br .br
.SH DESCRIPTION .SH DESCRIPTION
This manual page documents the This manual page documents the
@ -12,9 +12,7 @@ This manual page documents the
command. command.
.PP .PP
\fBcwebp\fP compresses an image using the WebP format. \fBcwebp\fP compresses an image using the WebP format.
Input format can be either PNG, JPEG, or raw Y'CbCr samples. Input format can be either PNG, JPEG, TIFF or raw Y'CbCr samples.
When using PNG, the transparency information (alpha channel) is currently
discarded.
.SH OPTIONS .SH OPTIONS
The basic options are: The basic options are:
.TP .TP
@ -32,15 +30,20 @@ A summary of all the possible options.
Print the version number (as major.minor.revision) and exit. Print the version number (as major.minor.revision) and exit.
.TP .TP
.B \-q float .B \-q float
Specify the compression factor between 0 and 100. A small factor Specify the compression factor for RGB channels between 0 and 100. A small
produces a smaller file with lower quality. Best quality is achieved factor produces a smaller file with lower quality. Best quality is achieved
using a value of 100. The default is 75. using a value of 100. The default is 75.
.TP .TP
.B \-alpha_q int
Specify the compression factor for alpha compression between 0 and 100.
Lossless compression of alpha is achieved using a value of 100, while the lower
values result in a lossy compression. The default is 100.
.TP
.B \-f int .B \-f int
Specify the strength of the deblocking filter, between 0 (no filtering) Specify the strength of the deblocking filter, between 0 (no filtering)
and 100 (maximum filtering). A value of 0 will turn off any filtering. and 100 (maximum filtering). A value of 0 will turn off any filtering.
Higher value will increase the strength of the filtering process applied Higher value will increase the strength of the filtering process applied
after decoding the picture. The higher the smoother the picture will after decoding the picture. The higher the value the smoother the picture will
appear. Typical values are usually in the range of 20 to 50. appear. Typical values are usually in the range of 20 to 50.
.TP .TP
.B \-preset string .B \-preset string
@ -60,12 +63,12 @@ and where else to better transfer these bits. The possible range goes from
.TP .TP
.B \-m int .B \-m int
Specify the compression method to use. This parameter controls the Specify the compression method to use. This parameter controls the
tradeoff between encoding speed and the compressed file size and quality. trade off between encoding speed and the compressed file size and quality.
Possible values range from 0 to 6. Default value is 4. Possible values range from 0 to 6. Default value is 4.
When higher values are used, the encoder will spend more time inspecting When higher values are used, the encoder will spend more time inspecting
additional encoding possibilities and decide on the quality gain. additional encoding possibilities and decide on the quality gain.
Lower value can result is faster processing time at the expense of Lower value can result is faster processing time at the expense of
larger filesize and lower compression quality. larger file size and lower compression quality.
.TP .TP
.B \-af .B \-af
Turns auto-filter on. This algorithm will spend additional time optimizing Turns auto-filter on. This algorithm will spend additional time optimizing
@ -116,7 +119,7 @@ Compressor will make several pass of partial encoding in order to get as
close as possible to this target. close as possible to this target.
.TP .TP
.B \-pass int .B \-pass int
Set a maximum number of pass to use during the dichotomy used by Set a maximum number of passes to use during the dichotomy used by
options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10. options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10.
.TP .TP
.B \-crop x_position y_position width height .B \-crop x_position y_position width height
@ -137,33 +140,77 @@ range from 1 to 6. This is only meant to help debugging.
Specify a pre-processing filter. This option is a placeholder Specify a pre-processing filter. This option is a placeholder
and has currently no effect. and has currently no effect.
.TP .TP
.B \-alpha_filter string
Specify the predictive filtering method for the alpha plane. One of 'none',
\&'fast' or 'best', in increasing complexity and slowness order. Default is
\&'fast'. Internally, alpha filtering is performed using four possible
predictions (none, horizontal, vertical, gradient). The 'best' mode will try
each mode in turn and pick the one which gives the smaller size. The 'fast'
mode will just try to form an a-priori guess without testing all modes.
.TP
.B \-alpha_method int
Specify the algorithm used for alpha compression: 0 or 1. Algorithm 0 denotes
no compression, 1 uses WebP lossless format for compression. The default is 1.
.TP
.B \-alpha_cleanup
Modify unseen RGB values under fully transparent area, to help compressibility.
The default is off.
.TP
.B \-noalpha
Using this option will discard the alpha channel.
.TP
.B \-lossless
Encode the image without any loss.
.TP
.B \-hint string
Specify the hint about input image type. Possible values are:
\fBphoto\fP, \fBpicture\fP or \fBgraph\fP.
.TP
.B \-noasm .B \-noasm
Disable all assembly optimizations. Disable all assembly optimizations.
.TP .TP
.B \-v .B \-v
Print extra information (encoding time in particular). Print extra information (encoding time in particular).
.TP .TP
.B \-print_psnr
Compute and report average PSNR (Peak-Signal-To-Noise ratio).
.TP
.B \-print_ssim
Compute and report average SSIM (structural similarity metric)
.TP
.B \-progress
Report encoding progress in percent.
.TP
.B \-quiet .B \-quiet
Do not print anything. Do not print anything.
.TP .TP
.B \-short .B \-short
Only print brief information (output file size and PSNR) for testing purpose. Only print brief information (output file size and PSNR) for testing purpose.
.SH Examples: .SH BUGS
cwebp -q 70 picture.png -o picture.webp Please report all bugs to our issue tracker:
http://code.google.com/p/webp/issues
.br .br
cwebp -sns 70 -f 50 -strong -af -size 60000 picture.png -o picture.webp Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting-patches/
.SH .SH EXAMPLES
.SH SEE ALSO cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp
.BR dwebp (1).
.br .br
Please refer to http://code.google.com/speed/webp/ for additional cwebp \-q 70 picture_with_alpha.png \-o picture_with_alpha.webp
information. .br
.SH AUTHOR cwebp \-sns 70 \-f 50 \-strong \-af \-size 60000 picture.png \-o picture.webp
.SH AUTHORS
\fBcwebp\fP was written by the WebP team. \fBcwebp\fP was written by the WebP team.
.br .br
The latest source tree is available at http://www.webmproject.org/code The latest source tree is available at http://www.webmproject.org/code
.PP .PP
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>, This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
for the Debian project (and may be used by others). for the Debian project (and may be used by others).
.SH SEE ALSO
.BR dwebp (1).
.br
Please refer to http://developers.google.com/speed/webp/ for additional
information.

View File

@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*- .\" Hey, EMACS: -*- nroff -*-
.TH DWEBP 1 "September 19, 2011" .TH DWEBP 1 "August 2, 2012"
.SH NAME .SH NAME
dwebp \- decompress a WebP file to an image file dwebp \- decompress a WebP file to an image file
.SH SYNOPSIS .SH SYNOPSIS
@ -11,7 +11,7 @@ This manual page documents the
.B dwebp .B dwebp
command. command.
.PP .PP
\fBdwebp\fP decompresses WebP files into PNG, PPM or PGM images. \fBdwebp\fP decompresses WebP files into PNG, PAM, PPM or PGM images.
.SH OPTIONS .SH OPTIONS
The basic options are: The basic options are:
.TP .TP
@ -24,8 +24,11 @@ Print the version number (as major.minor.revision) and exit.
.B \-o string .B \-o string
Specify the name of the output file (as PNG format by default). Specify the name of the output file (as PNG format by default).
.TP .TP
.B \-pam
Change the output format to PAM (retains alpha).
.TP
.B \-ppm .B \-ppm
Change the output format to PPM. Change the output format to PPM (discards alpha).
.TP .TP
.B \-pgm .B \-pgm
Change the output format to PGM. The output consist of luma/chroma Change the output format to PGM. The output consist of luma/chroma
@ -53,10 +56,10 @@ This option is meant to reduce the memory needed for cropping large images.
Note: the cropping is applied \fIbefore\fP any scaling. Note: the cropping is applied \fIbefore\fP any scaling.
.TP .TP
.B \-scale width height .B \-scale width height
Rescale the decoded picture to dimension \fBwidth\fP x \fBheight\fP. This option is Rescale the decoded picture to dimension \fBwidth\fP x \fBheight\fP. This
mostly intended to reducing the memory needed to decode large images, option is mostly intended to reducing the memory needed to decode large images,
when only a small version is needed (thumbnail, preview, etc.). when only a small version is needed (thumbnail, preview, etc.). Note: scaling
Note: scaling is applied \fIafter\fP cropping. is applied \fIafter\fP cropping.
.TP .TP
.B \-v .B \-v
Print extra information (decoding time in particular). Print extra information (decoding time in particular).
@ -64,21 +67,36 @@ Print extra information (decoding time in particular).
.B \-noasm .B \-noasm
Disable all assembly optimizations. Disable all assembly optimizations.
.SH Examples: .SH BUGS
dwebp picture.webp -o output.png Please report all bugs to our issue tracker:
http://code.google.com/p/webp/issues
.br .br
dwebp picture.webp -ppm -o output.ppm Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting-patches/
.SH .SH EXAMPLES
.SH SEE ALSO dwebp picture.webp \-o output.png
.BR cwebp (1).
.br .br
Please refer to http://code.google.com/speed/webp/ for additional dwebp picture.webp \-ppm \-o output.ppm
information.
.SH AUTHOR .SH AUTHORS
\fBdwebp\fP was written by the WebP team. \fBdwebp\fP was written by the WebP team.
.br .br
The latest source tree is available at http://www.webmproject.org/code The latest source tree is available at http://www.webmproject.org/code
.PP .PP
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>, This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
for the Debian project (and may be used by others). for the Debian project (and may be used by others).
.SH SEE ALSO
.BR cwebp (1).
.br
Please refer to http://developers.google.com/speed/webp/ for additional
information.
.SS Output file format details
PAM: http://netpbm.sourceforge.net/doc/pam.html
.br
PGM: http://netpbm.sourceforge.net/doc/pgm.html
.br
PPM: http://netpbm.sourceforge.net/doc/ppm.html
.br
PNG: http://www.libpng.org/pub/png/png-sitemap.html#info

135
man/webpmux.1 Normal file
View File

@ -0,0 +1,135 @@
.\" Hey, EMACS: -*- nroff -*-
.TH WEBPMUX 1 "January 24, 2012"
.SH NAME
webpmux \- command line tool to create WebP Mux/container file.
.SH SYNOPSIS
.B webpmux \-get
.I GET_OPTIONS
.I INPUT
.B \-o
.I OUTPUT
.br
.B webpmux \-set
.I SET_OPTIONS
.I INPUT
.B \-o
.I OUTPUT
.br
.B webpmux \-strip
.I STRIP_OPTIONS
.I INPUT
.B \-o
.I OUTPUT
.br
.B webpmux \-tile
.I TILE_OPTIONS
.B [\-tile...] \-o
.I OUTPUT
.br
.B webpmux \-frame
.I FRAME_OPTIONS
.B [\-frame...] \-loop
.I LOOP_COUNT
.B \-o
.I OUTPUT
.br
.B webpmux \-info
.I INPUT
.br
.B webpmux [\-h|\-help]
.SH DESCRIPTION
This manual page documents the
.B webpmux
command.
.PP
\fBwebpmux\fP can be used to create a WebP container file
and extract/strip relevant data from the container file.
.SH OPTIONS
.SS GET_OPTIONS (\-get):
.TP
.B icc
Get ICC Color profile.
.TP
.B xmp
Get XMP metadata.
.TP
.B tile n
Get nth tile.
.TP
.B frame n
Get nth frame.
.SS SET_OPTIONS (\-set)
.TP
.B icc
Set ICC Color profile.
.TP
.B xmp
Set XMP metadata.
.SS STRIP_OPTIONS (\-strip)
.TP
.B icc
Strip ICC Color profile.
.TP
.B xmp
Strip XMP metadata.
.SS TILE_OPTIONS (\-tile)
.TP
.B file_i +xi+yi
Where: 'file_i' is the i'th tile (webp format) and 'xi','yi' specify the image
offset for this tile.
.SS FRAME_OPTIONS (\-frame)
.TP
.B file_i +xi+yi+di
Where: 'file_i' is the i'th frame (webp format), 'xi','yi' specify the image
offset for this frame and 'di' is the pause duration before next frame.
.TP
.B \-loop n
Loop the frames n number of times. 0 indicates the frames should loop forever.
.SS INPUT
.TP
Input file in WebP format.
.SS OUTPUT (\-o)
.TP
Output file in WebP format.
.SH BUGS
Please report all bugs to our issue tracker:
http://code.google.com/p/webp/issues
.br
Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting-patches/
.SH EXAMPLES
webpmux \-set icc image_profile.icc in.webp \-o icc_container.webp
.br
webpmux \-get icc icc_container.webp \-o image_profile.icc
.br
webpmux \-set xmp image_metadata.xmp in.webp \-o xmp_container.webp
.br
webpmux \-get xmp xmp_container.webp \-o image_metadata.xmp
.br
webpmux \-frame anim_1.webp +0+0+0 \-frame anim_2.webp +50+50+0 \-loop 10
\-o anim_container.webp
.br
webpmux \-get frame 2 anim_container.webp \-o frame_2.webp
.SH AUTHORS
\fBwebpmux\fP is written by the WebP team.
.br
The latest source tree is available at http://www.webmproject.org/code
.PP
This manual page was written by Vikas Arora <vikaas.arora@gmail.com>,
for the Debian project (and may be used by others).
.SH SEE ALSO
.BR dwebp (1),
.BR cwebp (1).
.br
Please refer to http://developers.google.com/speed/webp/ for additional
information.

View File

@ -1,18 +1,27 @@
SUBDIRS = dec enc dsp utils SUBDIRS = dec enc dsp utils
if WANT_MUX
SUBDIRS += mux
endif
AM_CPPFLAGS = -I$(top_srcdir)/src AM_CPPFLAGS = -I$(top_srcdir)/src
lib_LTLIBRARIES = libwebp.la lib_LTLIBRARIES = libwebp.la
libwebp_la_SOURCES = libwebp_la_SOURCES =
libwebp_la_LIBADD = dec/libwebpdecode.la \ libwebpinclude_HEADERS =
enc/libwebpencode.la \ libwebpinclude_HEADERS += webp/decode.h
utils/libwebputils.la \ libwebpinclude_HEADERS += webp/encode.h
dsp/libwebpdsp.la libwebpinclude_HEADERS += webp/types.h
libwebp_la_LDFLAGS = -version-info 2:0:0 noinst_HEADERS =
libwebpinclude_HEADERS = webp/types.h webp/decode.h webp/decode_vp8.h \ noinst_HEADERS += webp/format_constants.h
webp/encode.h
libwebp_la_LIBADD =
libwebp_la_LIBADD += dec/libwebpdecode.la
libwebp_la_LIBADD += dsp/libwebpdsp.la
libwebp_la_LIBADD += enc/libwebpencode.la
libwebp_la_LIBADD += utils/libwebputils.la
libwebp_la_LDFLAGS = -version-info 4:0:0
libwebpincludedir = $(includedir)/webp libwebpincludedir = $(includedir)/webp
pkgconfig_DATA = libwebp.pc pkgconfig_DATA = libwebp.pc
${pkgconfig_DATA}: ${top_builddir}/config.status ${pkgconfig_DATA}: ${top_builddir}/config.status

View File

@ -1,13 +1,28 @@
AM_CPPFLAGS = -I$(top_srcdir)/src AM_CPPFLAGS = -I$(top_srcdir)/src
libwebpdecode_la_SOURCES = vp8i.h webpi.h \
frame.c quant.c tree.c vp8.c webp.c \
idec.c alpha.c layer.c io.c buffer.c
libwebpdecode_la_LDFLAGS = -version-info 2:0:0
libwebpdecode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h
libwebpdecodeincludedir = $(includedir)/webp
noinst_HEADERS = vp8i.h webpi.h
noinst_LTLIBRARIES = libwebpdecode.la noinst_LTLIBRARIES = libwebpdecode.la
libwebpdecode_la_SOURCES =
libwebpdecode_la_SOURCES += alpha.c
libwebpdecode_la_SOURCES += buffer.c
libwebpdecode_la_SOURCES += decode_vp8.h
libwebpdecode_la_SOURCES += frame.c
libwebpdecode_la_SOURCES += idec.c
libwebpdecode_la_SOURCES += io.c
libwebpdecode_la_SOURCES += layer.c
libwebpdecode_la_SOURCES += quant.c
libwebpdecode_la_SOURCES += tree.c
libwebpdecode_la_SOURCES += vp8.c
libwebpdecode_la_SOURCES += vp8i.h
libwebpdecode_la_SOURCES += vp8l.c
libwebpdecode_la_SOURCES += vp8li.h
libwebpdecode_la_SOURCES += webp.c
libwebpdecode_la_SOURCES += webpi.h
libwebpdecodeinclude_HEADERS =
libwebpdecodeinclude_HEADERS += ../webp/decode.h
libwebpdecodeinclude_HEADERS += ../webp/types.h
noinst_HEADERS =
noinst_HEADERS += ../webp/format_constants.h
libwebpdecode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
libwebpdecodeincludedir = $(includedir)/webp

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -10,60 +10,131 @@
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h> #include <stdlib.h>
#include "vp8i.h" #include "./vp8i.h"
#include "./vp8li.h"
#ifdef WEBP_EXPERIMENTAL_FEATURES #include "../utils/filters.h"
#include "../utils/quant_levels.h"
#include "zlib.h" #include "../webp/format_constants.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
// TODO(skal): move to dsp/ ?
static void CopyPlane(const uint8_t* src, int src_stride,
uint8_t* dst, int dst_stride, int width, int height) {
while (height-- > 0) {
memcpy(dst, src, width);
src += src_stride;
dst += dst_stride;
}
}
//------------------------------------------------------------------------------
// Decodes the compressed data 'data' of size 'data_size' into the 'output'.
// The 'output' buffer should be pre-allocated and must be of the same
// dimension 'height'x'stride', as that of the image.
//
// Returns 1 on successfully decoding the compressed alpha and
// 0 if either:
// error in bit-stream header (invalid compression mode or filter), or
// error returned by appropriate compression method.
static int DecodeAlpha(const uint8_t* data, size_t data_size,
int width, int height, int stride, uint8_t* output) {
uint8_t* decoded_data = NULL;
const size_t decoded_size = height * width;
uint8_t* unfiltered_data = NULL;
WEBP_FILTER_TYPE filter;
int pre_processing;
int rsrv;
int ok = 0;
int method;
assert(width > 0 && height > 0 && stride >= width);
assert(data != NULL && output != NULL);
if (data_size <= ALPHA_HEADER_LEN) {
return 0;
}
method = (data[0] >> 0) & 0x03;
filter = (data[0] >> 2) & 0x03;
pre_processing = (data[0] >> 4) & 0x03;
rsrv = (data[0] >> 6) & 0x03;
if (method < ALPHA_NO_COMPRESSION ||
method > ALPHA_LOSSLESS_COMPRESSION ||
filter >= WEBP_FILTER_LAST ||
pre_processing > ALPHA_PREPROCESSED_LEVELS ||
rsrv != 0) {
return 0;
}
if (method == ALPHA_NO_COMPRESSION) {
ok = (data_size >= decoded_size);
decoded_data = (uint8_t*)data + ALPHA_HEADER_LEN;
} else {
decoded_data = (uint8_t*)malloc(decoded_size);
if (decoded_data == NULL) return 0;
ok = VP8LDecodeAlphaImageStream(width, height,
data + ALPHA_HEADER_LEN,
data_size - ALPHA_HEADER_LEN,
decoded_data);
}
if (ok) {
WebPFilterFunc unfilter_func = WebPUnfilters[filter];
if (unfilter_func != NULL) {
unfiltered_data = (uint8_t*)malloc(decoded_size);
if (unfiltered_data == NULL) {
ok = 0;
goto Error;
}
// TODO(vikas): Implement on-the-fly decoding & filter mechanism to decode
// and apply filter per image-row.
unfilter_func(decoded_data, width, height, 1, width, unfiltered_data);
// Construct raw_data (height x stride) from alpha data (height x width).
CopyPlane(unfiltered_data, width, output, stride, width, height);
free(unfiltered_data);
} else {
// Construct raw_data (height x stride) from alpha data (height x width).
CopyPlane(decoded_data, width, output, stride, width, height);
}
if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
ok = DequantizeLevels(decoded_data, width, height);
}
}
Error:
if (method != ALPHA_NO_COMPRESSION) {
free(decoded_data);
}
return ok;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
int row, int num_rows) { int row, int num_rows) {
uint8_t* output = dec->alpha_plane_;
const int stride = dec->pic_hdr_.width_; const int stride = dec->pic_hdr_.width_;
if (row < 0 || row + num_rows > dec->pic_hdr_.height_) {
return NULL; // sanity check if (row < 0 || num_rows < 0 || row + num_rows > dec->pic_hdr_.height_) {
return NULL; // sanity check.
} }
if (row == 0) { if (row == 0) {
// TODO(skal): for now, we just decompress everything during the first call. // Decode everything during the first call.
// Later, we'll decode progressively, but we need to store the if (!DecodeAlpha(dec->alpha_data_, (size_t)dec->alpha_data_size_,
// z_stream state. dec->pic_hdr_.width_, dec->pic_hdr_.height_, stride,
const uint8_t* data = dec->alpha_data_; dec->alpha_plane_)) {
size_t data_size = dec->alpha_data_size_; return NULL; // Error.
const size_t output_size = stride * dec->pic_hdr_.height_;
int ret = Z_OK;
z_stream strm;
memset(&strm, 0, sizeof(strm));
if (inflateInit(&strm) != Z_OK) {
return 0;
}
strm.avail_in = data_size;
strm.next_in = (unsigned char*)data;
do {
strm.avail_out = output_size;
strm.next_out = output;
ret = inflate(&strm, Z_NO_FLUSH);
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
break;
}
} while (strm.avail_out == 0);
inflateEnd(&strm);
if (ret != Z_STREAM_END) {
return NULL; // error
} }
} }
return output + row * stride;
// Return a pointer to the current decoded row.
return dec->alpha_plane_ + row * stride;
} }
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif // WEBP_EXPERIMENTAL_FEATURES

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -10,8 +10,10 @@
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h> #include <stdlib.h>
#include "vp8i.h"
#include "webpi.h" #include "./vp8i.h"
#include "./webpi.h"
#include "../utils/utils.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -21,33 +23,51 @@ extern "C" {
// WebPDecBuffer // WebPDecBuffer
// Number of bytes per pixel for the different color-spaces. // Number of bytes per pixel for the different color-spaces.
static const int kModeBpp[MODE_LAST] = { 3, 4, 3, 4, 4, 2, 2, 1, 1 }; static const int kModeBpp[MODE_LAST] = {
3, 4, 3, 4, 4, 2, 2,
4, 4, 4, 2, // pre-multiplied modes
1, 1 };
// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
// Convert to an integer to handle both the unsigned/signed enum cases
// without the need for casting to remove type limit warnings.
static int IsValidColorspace(int webp_csp_mode) {
return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
}
static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) { static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
int ok = 1; int ok = 1;
WEBP_CSP_MODE mode = buffer->colorspace; const WEBP_CSP_MODE mode = buffer->colorspace;
const int width = buffer->width; const int width = buffer->width;
const int height = buffer->height; const int height = buffer->height;
if (mode >= MODE_YUV) { // YUV checks if (!IsValidColorspace(mode)) {
ok = 0;
} else if (!WebPIsRGBMode(mode)) { // YUV checks
const WebPYUVABuffer* const buf = &buffer->u.YUVA; const WebPYUVABuffer* const buf = &buffer->u.YUVA;
const int size = buf->y_stride * height; const uint64_t y_size = (uint64_t)buf->y_stride * height;
const int u_size = buf->u_stride * ((height + 1) / 2); const uint64_t u_size = (uint64_t)buf->u_stride * ((height + 1) / 2);
const int v_size = buf->v_stride * ((height + 1) / 2); const uint64_t v_size = (uint64_t)buf->v_stride * ((height + 1) / 2);
const int a_size = buf->a_stride * height; const uint64_t a_size = (uint64_t)buf->a_stride * height;
ok &= (size <= buf->y_size); ok &= (y_size <= buf->y_size);
ok &= (u_size <= buf->u_size); ok &= (u_size <= buf->u_size);
ok &= (v_size <= buf->v_size); ok &= (v_size <= buf->v_size);
ok &= (a_size <= buf->a_size);
ok &= (buf->y_stride >= width); ok &= (buf->y_stride >= width);
ok &= (buf->u_stride >= (width + 1) / 2); ok &= (buf->u_stride >= (width + 1) / 2);
ok &= (buf->v_stride >= (width + 1) / 2); ok &= (buf->v_stride >= (width + 1) / 2);
if (buf->a) { ok &= (buf->y != NULL);
ok &= (buf->u != NULL);
ok &= (buf->v != NULL);
if (mode == MODE_YUVA) {
ok &= (buf->a_stride >= width); ok &= (buf->a_stride >= width);
ok &= (a_size <= buf->a_size);
ok &= (buf->a != NULL);
} }
} else { // RGB checks } else { // RGB checks
const WebPRGBABuffer* const buf = &buffer->u.RGBA; const WebPRGBABuffer* const buf = &buffer->u.RGBA;
ok &= (buf->stride * height <= buf->size); const uint64_t size = (uint64_t)buf->stride * height;
ok &= (size <= buf->size);
ok &= (buf->stride >= width * kModeBpp[mode]); ok &= (buf->stride >= width * kModeBpp[mode]);
ok &= (buf->rgba != NULL);
} }
return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM; return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
} }
@ -55,24 +75,22 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
const int w = buffer->width; const int w = buffer->width;
const int h = buffer->height; const int h = buffer->height;
const WEBP_CSP_MODE mode = buffer->colorspace;
if (w <= 0 || h <= 0) { if (w <= 0 || h <= 0 || !IsValidColorspace(mode)) {
return VP8_STATUS_INVALID_PARAM; return VP8_STATUS_INVALID_PARAM;
} }
if (!buffer->is_external_memory && buffer->private_memory == NULL) { if (!buffer->is_external_memory && buffer->private_memory == NULL) {
uint8_t* output; uint8_t* output;
WEBP_CSP_MODE mode = buffer->colorspace;
int stride;
int uv_stride = 0, a_stride = 0; int uv_stride = 0, a_stride = 0;
int uv_size = 0; uint64_t uv_size = 0, a_size = 0, total_size;
uint64_t size, a_size = 0, total_size;
// We need memory and it hasn't been allocated yet. // We need memory and it hasn't been allocated yet.
// => initialize output buffer, now that dimensions are known. // => initialize output buffer, now that dimensions are known.
stride = w * kModeBpp[mode]; const int stride = w * kModeBpp[mode];
size = (uint64_t)stride * h; const uint64_t size = (uint64_t)stride * h;
if (mode >= MODE_YUV) { if (!WebPIsRGBMode(mode)) {
uv_stride = (w + 1) / 2; uv_stride = (w + 1) / 2;
uv_size = (uint64_t)uv_stride * ((h + 1) / 2); uv_size = (uint64_t)uv_stride * ((h + 1) / 2);
if (mode == MODE_YUVA) { if (mode == MODE_YUVA) {
@ -83,36 +101,33 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
total_size = size + 2 * uv_size + a_size; total_size = size + 2 * uv_size + a_size;
// Security/sanity checks // Security/sanity checks
if (((size_t)total_size != total_size) || (total_size >= (1ULL << 40))) { output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output));
return VP8_STATUS_INVALID_PARAM;
}
buffer->private_memory = output = (uint8_t*)malloc((size_t)total_size);
if (output == NULL) { if (output == NULL) {
return VP8_STATUS_OUT_OF_MEMORY; return VP8_STATUS_OUT_OF_MEMORY;
} }
buffer->private_memory = output;
if (mode >= MODE_YUV) { // YUVA initialization if (!WebPIsRGBMode(mode)) { // YUVA initialization
WebPYUVABuffer* const buf = &buffer->u.YUVA; WebPYUVABuffer* const buf = &buffer->u.YUVA;
buf->y = output; buf->y = output;
buf->y_stride = stride; buf->y_stride = stride;
buf->y_size = size; buf->y_size = (size_t)size;
buf->u = output + size; buf->u = output + size;
buf->u_stride = uv_stride; buf->u_stride = uv_stride;
buf->u_size = uv_size; buf->u_size = (size_t)uv_size;
buf->v = output + size + uv_size; buf->v = output + size + uv_size;
buf->v_stride = uv_stride; buf->v_stride = uv_stride;
buf->v_size = uv_size; buf->v_size = (size_t)uv_size;
if (mode == MODE_YUVA) { if (mode == MODE_YUVA) {
buf->a = output + size + 2 * uv_size; buf->a = output + size + 2 * uv_size;
} }
buf->a_size = a_size; buf->a_size = (size_t)a_size;
buf->a_stride = a_stride; buf->a_stride = a_stride;
} else { // RGBA initialization } else { // RGBA initialization
WebPRGBABuffer* const buf = &buffer->u.RGBA; WebPRGBABuffer* const buf = &buffer->u.RGBA;
buf->rgba = output; buf->rgba = output;
buf->stride = stride; buf->stride = stride;
buf->size = size; buf->size = (size_t)size;
} }
} }
return CheckDecBuffer(buffer); return CheckDecBuffer(buffer);
@ -140,7 +155,7 @@ VP8StatusCode WebPAllocateDecBuffer(int w, int h,
if (options->scaled_width <= 0 || options->scaled_height <= 0) { if (options->scaled_width <= 0 || options->scaled_height <= 0) {
return VP8_STATUS_INVALID_PARAM; return VP8_STATUS_INVALID_PARAM;
} }
w = options->scaled_width; w = options->scaled_width;
h = options->scaled_height; h = options->scaled_height;
} }
} }
@ -154,15 +169,17 @@ VP8StatusCode WebPAllocateDecBuffer(int w, int h,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// constructors / destructors // constructors / destructors
int WebPInitDecBufferInternal(WebPDecBuffer* const buffer, int version) { int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
if (version != WEBP_DECODER_ABI_VERSION) return 0; // version mismatch if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
if (!buffer) return 0; return 0; // version mismatch
}
if (buffer == NULL) return 0;
memset(buffer, 0, sizeof(*buffer)); memset(buffer, 0, sizeof(*buffer));
return 1; return 1;
} }
void WebPFreeDecBuffer(WebPDecBuffer* const buffer) { void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
if (buffer) { if (buffer != NULL) {
if (!buffer->is_external_memory) if (!buffer->is_external_memory)
free(buffer->private_memory); free(buffer->private_memory);
buffer->private_memory = NULL; buffer->private_memory = NULL;
@ -171,9 +188,9 @@ void WebPFreeDecBuffer(WebPDecBuffer* const buffer) {
void WebPCopyDecBuffer(const WebPDecBuffer* const src, void WebPCopyDecBuffer(const WebPDecBuffer* const src,
WebPDecBuffer* const dst) { WebPDecBuffer* const dst) {
if (src && dst) { if (src != NULL && dst != NULL) {
*dst = *src; *dst = *src;
if (src->private_memory) { if (src->private_memory != NULL) {
dst->is_external_memory = 1; // dst buffer doesn't own the memory. dst->is_external_memory = 1; // dst buffer doesn't own the memory.
dst->private_memory = NULL; dst->private_memory = NULL;
} }
@ -182,9 +199,9 @@ void WebPCopyDecBuffer(const WebPDecBuffer* const src,
// Copy and transfer ownership from src to dst (beware of parameter order!) // Copy and transfer ownership from src to dst (beware of parameter order!)
void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) { void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
if (src && dst) { if (src != NULL && dst != NULL) {
*dst = *src; *dst = *src;
if (src->private_memory) { if (src->private_memory != NULL) {
src->is_external_memory = 1; // src relinquishes ownership src->is_external_memory = 1; // src relinquishes ownership
src->private_memory = NULL; src->private_memory = NULL;
} }

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -12,7 +12,7 @@
#ifndef WEBP_WEBP_DECODE_VP8_H_ #ifndef WEBP_WEBP_DECODE_VP8_H_
#define WEBP_WEBP_DECODE_VP8_H_ #define WEBP_WEBP_DECODE_VP8_H_
#include "./decode.h" #include "../webp/decode.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -82,7 +82,7 @@ struct VP8Io {
int fancy_upsampling; int fancy_upsampling;
// Input buffer. // Input buffer.
uint32_t data_size; size_t data_size;
const uint8_t* data; const uint8_t* data;
// If true, in-loop filtering will not be performed even if present in the // If true, in-loop filtering will not be performed even if present in the
@ -99,56 +99,81 @@ struct VP8Io {
int use_scaling; int use_scaling;
int scaled_width, scaled_height; int scaled_width, scaled_height;
// pointer to the alpha data (if present) corresponding to the rows // If non NULL, pointer to the alpha data (if present) corresponding to the
// start of the current row (That is: it is pre-offset by mb_y and takes
// cropping into account).
const uint8_t* a; const uint8_t* a;
}; };
// Internal, version-checked, entry point // Internal, version-checked, entry point
WEBP_EXTERN(int) VP8InitIoInternal(VP8Io* const, int); int VP8InitIoInternal(VP8Io* const, int);
// Set the custom IO function pointers and user-data. The setter for IO hooks // Set the custom IO function pointers and user-data. The setter for IO hooks
// should be called before initiating incremental decoding. Returns true if // should be called before initiating incremental decoding. Returns true if
// WebPIDecoder object is successfully modified, false otherwise. // WebPIDecoder object is successfully modified, false otherwise.
WEBP_EXTERN(int) WebPISetIOHooks(WebPIDecoder* const idec, int WebPISetIOHooks(WebPIDecoder* const idec,
VP8IoPutHook put, VP8IoPutHook put,
VP8IoSetupHook setup, VP8IoSetupHook setup,
VP8IoTeardownHook teardown, VP8IoTeardownHook teardown,
void* user_data); void* user_data);
// Main decoding object. This is an opaque structure. // Main decoding object. This is an opaque structure.
typedef struct VP8Decoder VP8Decoder; typedef struct VP8Decoder VP8Decoder;
// Create a new decoder object. // Create a new decoder object.
WEBP_EXTERN(VP8Decoder*) VP8New(void); VP8Decoder* VP8New(void);
// Must be called to make sure 'io' is initialized properly. // Must be called to make sure 'io' is initialized properly.
// Returns false in case of version mismatch. Upon such failure, no other // Returns false in case of version mismatch. Upon such failure, no other
// decoding function should be called (VP8Decode, VP8GetHeaders, ...) // decoding function should be called (VP8Decode, VP8GetHeaders, ...)
static inline int VP8InitIo(VP8Io* const io) { static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION); return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION);
} }
// Start decoding a new picture. Returns true if ok. // Start decoding a new picture. Returns true if ok.
WEBP_EXTERN(int) VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io); int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
// Decode a picture. Will call VP8GetHeaders() if it wasn't done already. // Decode a picture. Will call VP8GetHeaders() if it wasn't done already.
// Returns false in case of error. // Returns false in case of error.
WEBP_EXTERN(int) VP8Decode(VP8Decoder* const dec, VP8Io* const io); int VP8Decode(VP8Decoder* const dec, VP8Io* const io);
// Return current status of the decoder: // Return current status of the decoder:
WEBP_EXTERN(VP8StatusCode) VP8Status(VP8Decoder* const dec); VP8StatusCode VP8Status(VP8Decoder* const dec);
// return readable string corresponding to the last status. // return readable string corresponding to the last status.
WEBP_EXTERN(const char*) VP8StatusMessage(VP8Decoder* const dec); const char* VP8StatusMessage(VP8Decoder* const dec);
// Resets the decoder in its initial state, reclaiming memory. // Resets the decoder in its initial state, reclaiming memory.
// Not a mandatory call between calls to VP8Decode(). // Not a mandatory call between calls to VP8Decode().
WEBP_EXTERN(void) VP8Clear(VP8Decoder* const dec); void VP8Clear(VP8Decoder* const dec);
// Destroy the decoder object. // Destroy the decoder object.
WEBP_EXTERN(void) VP8Delete(VP8Decoder* const dec); void VP8Delete(VP8Decoder* const dec);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Miscellaneous VP8/VP8L bitstream probing functions.
// Returns true if the next 3 bytes in data contain the VP8 signature.
WEBP_EXTERN(int) VP8CheckSignature(const uint8_t* const data, size_t data_size);
// Validates the VP8 data-header and retrieves basic header information viz
// width and height. Returns 0 in case of formatting error. *width/*height
// can be passed NULL.
WEBP_EXTERN(int) VP8GetInfo(
const uint8_t* data,
size_t data_size, // data available so far
size_t chunk_size, // total data size expected in the chunk
int* const width, int* const height);
// Returns true if the next byte(s) in data is a VP8L signature.
WEBP_EXTERN(int) VP8LCheckSignature(const uint8_t* const data, size_t size);
// Validates the VP8L data-header and retrieves basic header information viz
// width, height and alpha. Returns 0 in case of formatting error.
// width/height/has_alpha can be passed NULL.
WEBP_EXTERN(int) VP8LGetInfo(
const uint8_t* data, size_t data_size, // data available so far
int* const width, int* const height, int* const has_alpha);
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -11,6 +11,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "./vp8i.h" #include "./vp8i.h"
#include "../utils/utils.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -19,54 +20,7 @@ extern "C" {
#define ALIGN_MASK (32 - 1) #define ALIGN_MASK (32 - 1)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line. // Filtering
//
// Reason is: the deblocking filter cannot deblock the bottom horizontal edges
// immediately, and needs to wait for first few rows of the next macroblock to
// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending
// on strength).
// With two threads, the vertical positions of the rows being decoded are:
// Decode: [ 0..15][16..31][32..47][48..63][64..79][...
// Deblock: [ 0..11][12..27][28..43][44..59][...
// If we use two threads and two caches of 16 pixels, the sequence would be:
// Decode: [ 0..15][16..31][ 0..15!!][16..31][ 0..15][...
// Deblock: [ 0..11][12..27!!][-4..11][12..27][...
// The problem occurs during row [12..15!!] that both the decoding and
// deblocking threads are writing simultaneously.
// With 3 cache lines, one get a safe write pattern:
// Decode: [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0..
// Deblock: [ 0..11][12..27][28..43][-4..11][12..27][28...
// Note that multi-threaded output _without_ deblocking can make use of two
// cache lines of 16 pixels only, since there's no lagging behind. The decoding
// and output process have non-concurrent writing:
// Decode: [ 0..15][16..31][ 0..15][16..31][...
// io->put: [ 0..15][16..31][ 0..15][...
#define MT_CACHE_LINES 3
#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
// Initialize multi/single-thread worker
static int InitThreadContext(VP8Decoder* const dec) {
dec->cache_id_ = 0;
if (dec->use_threads_) {
WebPWorker* const worker = &dec->worker_;
if (!WebPWorkerReset(worker)) {
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
"thread initialization failed.");
}
worker->data1 = dec;
worker->data2 = (void*)&dec->thread_ctx_.io_;
worker->hook = (WebPWorkerHook)VP8FinishRow;
dec->num_caches_ =
(dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
} else {
dec->num_caches_ = ST_CACHE_LINES;
}
return 1;
}
//------------------------------------------------------------------------------
// Memory setup
// kFilterExtraRows[] = How many extra lines are needed on the MB boundary // kFilterExtraRows[] = How many extra lines are needed on the MB boundary
// for caching, given a filtering level. // for caching, given a filtering level.
@ -75,125 +29,7 @@ static int InitThreadContext(VP8Decoder* const dec) {
// U/V, so it's 8 samples total (because of the 2x upsampling). // U/V, so it's 8 samples total (because of the 2x upsampling).
static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 }; static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
static int AllocateMemory(VP8Decoder* const dec) { static WEBP_INLINE int hev_thresh_from_level(int level, int keyframe) {
const int num_caches = dec->num_caches_;
const int mb_w = dec->mb_w_;
const int intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
const int top_size = (16 + 8 + 8) * mb_w;
const int mb_info_size = (mb_w + 1) * sizeof(VP8MB);
const int f_info_size =
(dec->filter_type_ > 0) ?
mb_w * (dec->use_threads_ ? 2 : 1) * sizeof(VP8FInfo)
: 0;
const int yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
const int coeffs_size = 384 * sizeof(*dec->coeffs_);
const int cache_height = (16 * num_caches
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
const int cache_size = top_size * cache_height;
const int alpha_size =
dec->alpha_data_ ? (dec->pic_hdr_.width_ * dec->pic_hdr_.height_) : 0;
const int needed = intra_pred_mode_size
+ top_size + mb_info_size + f_info_size
+ yuv_size + coeffs_size
+ cache_size + alpha_size + ALIGN_MASK;
uint8_t* mem;
if (needed > dec->mem_size_) {
free(dec->mem_);
dec->mem_size_ = 0;
dec->mem_ = (uint8_t*)malloc(needed);
if (dec->mem_ == NULL) {
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
"no memory during frame initialization.");
}
dec->mem_size_ = needed;
}
mem = (uint8_t*)dec->mem_;
dec->intra_t_ = (uint8_t*)mem;
mem += intra_pred_mode_size;
dec->y_t_ = (uint8_t*)mem;
mem += 16 * mb_w;
dec->u_t_ = (uint8_t*)mem;
mem += 8 * mb_w;
dec->v_t_ = (uint8_t*)mem;
mem += 8 * mb_w;
dec->mb_info_ = ((VP8MB*)mem) + 1;
mem += mb_info_size;
dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL;
mem += f_info_size;
dec->thread_ctx_.id_ = 0;
dec->thread_ctx_.f_info_ = dec->f_info_;
if (dec->use_threads_) {
// secondary cache line. The deblocking process need to make use of the
// filtering strength from previous macroblock row, while the new ones
// are being decoded in parallel. We'll just swap the pointers.
dec->thread_ctx_.f_info_ += mb_w;
}
mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
assert((yuv_size & ALIGN_MASK) == 0);
dec->yuv_b_ = (uint8_t*)mem;
mem += yuv_size;
dec->coeffs_ = (int16_t*)mem;
mem += coeffs_size;
dec->cache_y_stride_ = 16 * mb_w;
dec->cache_uv_stride_ = 8 * mb_w;
{
const int extra_rows = kFilterExtraRows[dec->filter_type_];
const int extra_y = extra_rows * dec->cache_y_stride_;
const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_;
dec->cache_y_ = ((uint8_t*)mem) + extra_y;
dec->cache_u_ = dec->cache_y_
+ 16 * num_caches * dec->cache_y_stride_ + extra_uv;
dec->cache_v_ = dec->cache_u_
+ 8 * num_caches * dec->cache_uv_stride_ + extra_uv;
dec->cache_id_ = 0;
}
mem += cache_size;
// alpha plane
dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL;
mem += alpha_size;
// note: left-info is initialized once for all.
memset(dec->mb_info_ - 1, 0, mb_info_size);
// initialize top
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
return 1;
}
static void InitIo(VP8Decoder* const dec, VP8Io* io) {
// prepare 'io'
io->mb_y = 0;
io->y = dec->cache_y_;
io->u = dec->cache_u_;
io->v = dec->cache_v_;
io->y_stride = dec->cache_y_stride_;
io->uv_stride = dec->cache_uv_stride_;
io->fancy_upsampling = 0; // default
io->a = NULL;
}
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
if (!AllocateMemory(dec)) return 0;
InitIo(dec, io);
VP8DspInit(); // Init critical function pointers and look-up tables.
return 1;
}
//------------------------------------------------------------------------------
// Filtering
static inline int hev_thresh_from_level(int level, int keyframe) {
if (keyframe) { if (keyframe) {
return (level >= 40) ? 2 : (level >= 15) ? 1 : 0; return (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
} else { } else {
@ -326,7 +162,7 @@ void VP8StoreBlock(VP8Decoder* const dec) {
#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB #define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
// Finalize and transmit a complete row. Return false in case of user-abort. // Finalize and transmit a complete row. Return false in case of user-abort.
int VP8FinishRow(VP8Decoder* const dec, VP8Io* io) { static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
int ok = 1; int ok = 1;
const VP8ThreadContext* const ctx = &dec->thread_ctx_; const VP8ThreadContext* const ctx = &dec->thread_ctx_;
const int extra_y_rows = kFilterExtraRows[dec->filter_type_]; const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
@ -365,15 +201,18 @@ int VP8FinishRow(VP8Decoder* const dec, VP8Io* io) {
y_end = io->crop_bottom; // make sure we don't overflow on last row. y_end = io->crop_bottom; // make sure we don't overflow on last row.
} }
io->a = NULL; io->a = NULL;
#ifdef WEBP_EXPERIMENTAL_FEATURES if (dec->alpha_data_ != NULL && y_start < y_end) {
if (dec->alpha_data_) { // TODO(skal): several things to correct here:
// * testing presence of alpha with dec->alpha_data_ is not a good idea
// * we're actually decompressing the full plane only once. It should be
// more obvious from signature.
// * we could free alpha_data_ right after this call, but we don't own.
io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start); io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start);
if (io->a == NULL) { if (io->a == NULL) {
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
"Could not decode alpha data."); "Could not decode alpha data.");
} }
} }
#endif
if (y_start < io->crop_top) { if (y_start < io->crop_top) {
const int delta_y = io->crop_top - y_start; const int delta_y = io->crop_top - y_start;
y_start = io->crop_top; y_start = io->crop_top;
@ -381,7 +220,7 @@ int VP8FinishRow(VP8Decoder* const dec, VP8Io* io) {
io->y += dec->cache_y_stride_ * delta_y; io->y += dec->cache_y_stride_ * delta_y;
io->u += dec->cache_uv_stride_ * (delta_y >> 1); io->u += dec->cache_uv_stride_ * (delta_y >> 1);
io->v += dec->cache_uv_stride_ * (delta_y >> 1); io->v += dec->cache_uv_stride_ * (delta_y >> 1);
if (io->a) { if (io->a != NULL) {
io->a += io->width * delta_y; io->a += io->width * delta_y;
} }
} }
@ -389,7 +228,7 @@ int VP8FinishRow(VP8Decoder* const dec, VP8Io* io) {
io->y += io->crop_left; io->y += io->crop_left;
io->u += io->crop_left >> 1; io->u += io->crop_left >> 1;
io->v += io->crop_left >> 1; io->v += io->crop_left >> 1;
if (io->a) { if (io->a != NULL) {
io->a += io->crop_left; io->a += io->crop_left;
} }
io->mb_y = y_start - io->crop_top; io->mb_y = y_start - io->crop_top;
@ -421,7 +260,7 @@ int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
// ctx->id_ and ctx->f_info_ are already set // ctx->id_ and ctx->f_info_ are already set
ctx->mb_y_ = dec->mb_y_; ctx->mb_y_ = dec->mb_y_;
ctx->filter_row_ = dec->filter_row_; ctx->filter_row_ = dec->filter_row_;
ok = VP8FinishRow(dec, io); ok = FinishRow(dec, io);
} else { } else {
WebPWorker* const worker = &dec->worker_; WebPWorker* const worker = &dec->worker_;
// Finish previous job *before* updating context // Finish previous job *before* updating context
@ -482,8 +321,13 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
dec->tl_mb_y_ = 0; dec->tl_mb_y_ = 0;
} else { } else {
// For simple filter, we can filter only the cropped region. // For simple filter, we can filter only the cropped region.
dec->tl_mb_y_ = io->crop_top >> 4; // We include 'extra_pixels' on the other side of the boundary, since
dec->tl_mb_x_ = io->crop_left >> 4; // vertical or horizontal filtering of the previous macroblock can
// modify some abutting pixels.
dec->tl_mb_x_ = (io->crop_left - extra_pixels) >> 4;
dec->tl_mb_y_ = (io->crop_top - extra_pixels) >> 4;
if (dec->tl_mb_x_ < 0) dec->tl_mb_x_ = 0;
if (dec->tl_mb_y_ < 0) dec->tl_mb_y_ = 0;
} }
// We need some 'extra' pixels on the right/bottom. // We need some 'extra' pixels on the right/bottom.
dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4; dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4;
@ -510,6 +354,177 @@ int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
return ok; return ok;
} }
//------------------------------------------------------------------------------
// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line.
//
// Reason is: the deblocking filter cannot deblock the bottom horizontal edges
// immediately, and needs to wait for first few rows of the next macroblock to
// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending
// on strength).
// With two threads, the vertical positions of the rows being decoded are:
// Decode: [ 0..15][16..31][32..47][48..63][64..79][...
// Deblock: [ 0..11][12..27][28..43][44..59][...
// If we use two threads and two caches of 16 pixels, the sequence would be:
// Decode: [ 0..15][16..31][ 0..15!!][16..31][ 0..15][...
// Deblock: [ 0..11][12..27!!][-4..11][12..27][...
// The problem occurs during row [12..15!!] that both the decoding and
// deblocking threads are writing simultaneously.
// With 3 cache lines, one get a safe write pattern:
// Decode: [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0..
// Deblock: [ 0..11][12..27][28..43][-4..11][12..27][28...
// Note that multi-threaded output _without_ deblocking can make use of two
// cache lines of 16 pixels only, since there's no lagging behind. The decoding
// and output process have non-concurrent writing:
// Decode: [ 0..15][16..31][ 0..15][16..31][...
// io->put: [ 0..15][16..31][ 0..15][...
#define MT_CACHE_LINES 3
#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
// Initialize multi/single-thread worker
static int InitThreadContext(VP8Decoder* const dec) {
dec->cache_id_ = 0;
if (dec->use_threads_) {
WebPWorker* const worker = &dec->worker_;
if (!WebPWorkerReset(worker)) {
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
"thread initialization failed.");
}
worker->data1 = dec;
worker->data2 = (void*)&dec->thread_ctx_.io_;
worker->hook = (WebPWorkerHook)FinishRow;
dec->num_caches_ =
(dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
} else {
dec->num_caches_ = ST_CACHE_LINES;
}
return 1;
}
#undef MT_CACHE_LINES
#undef ST_CACHE_LINES
//------------------------------------------------------------------------------
// Memory setup
static int AllocateMemory(VP8Decoder* const dec) {
const int num_caches = dec->num_caches_;
const int mb_w = dec->mb_w_;
// Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise.
const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
const size_t top_size = (16 + 8 + 8) * mb_w;
const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
const size_t f_info_size =
(dec->filter_type_ > 0) ?
mb_w * (dec->use_threads_ ? 2 : 1) * sizeof(VP8FInfo)
: 0;
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
const size_t coeffs_size = 384 * sizeof(*dec->coeffs_);
const size_t cache_height = (16 * num_caches
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
const size_t cache_size = top_size * cache_height;
// alpha_size is the only one that scales as width x height.
const uint64_t alpha_size = (dec->alpha_data_ != NULL) ?
(uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
const uint64_t needed = (uint64_t)intra_pred_mode_size
+ top_size + mb_info_size + f_info_size
+ yuv_size + coeffs_size
+ cache_size + alpha_size + ALIGN_MASK;
uint8_t* mem;
if (needed != (size_t)needed) return 0; // check for overflow
if (needed > dec->mem_size_) {
free(dec->mem_);
dec->mem_size_ = 0;
dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
if (dec->mem_ == NULL) {
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
"no memory during frame initialization.");
}
// down-cast is ok, thanks to WebPSafeAlloc() above.
dec->mem_size_ = (size_t)needed;
}
mem = (uint8_t*)dec->mem_;
dec->intra_t_ = (uint8_t*)mem;
mem += intra_pred_mode_size;
dec->y_t_ = (uint8_t*)mem;
mem += 16 * mb_w;
dec->u_t_ = (uint8_t*)mem;
mem += 8 * mb_w;
dec->v_t_ = (uint8_t*)mem;
mem += 8 * mb_w;
dec->mb_info_ = ((VP8MB*)mem) + 1;
mem += mb_info_size;
dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL;
mem += f_info_size;
dec->thread_ctx_.id_ = 0;
dec->thread_ctx_.f_info_ = dec->f_info_;
if (dec->use_threads_) {
// secondary cache line. The deblocking process need to make use of the
// filtering strength from previous macroblock row, while the new ones
// are being decoded in parallel. We'll just swap the pointers.
dec->thread_ctx_.f_info_ += mb_w;
}
mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
assert((yuv_size & ALIGN_MASK) == 0);
dec->yuv_b_ = (uint8_t*)mem;
mem += yuv_size;
dec->coeffs_ = (int16_t*)mem;
mem += coeffs_size;
dec->cache_y_stride_ = 16 * mb_w;
dec->cache_uv_stride_ = 8 * mb_w;
{
const int extra_rows = kFilterExtraRows[dec->filter_type_];
const int extra_y = extra_rows * dec->cache_y_stride_;
const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_;
dec->cache_y_ = ((uint8_t*)mem) + extra_y;
dec->cache_u_ = dec->cache_y_
+ 16 * num_caches * dec->cache_y_stride_ + extra_uv;
dec->cache_v_ = dec->cache_u_
+ 8 * num_caches * dec->cache_uv_stride_ + extra_uv;
dec->cache_id_ = 0;
}
mem += cache_size;
// alpha plane
dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL;
mem += alpha_size;
// note: left-info is initialized once for all.
memset(dec->mb_info_ - 1, 0, mb_info_size);
// initialize top
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
return 1;
}
static void InitIo(VP8Decoder* const dec, VP8Io* io) {
// prepare 'io'
io->mb_y = 0;
io->y = dec->cache_y_;
io->u = dec->cache_u_;
io->v = dec->cache_v_;
io->y_stride = dec->cache_y_stride_;
io->uv_stride = dec->cache_uv_stride_;
io->a = NULL;
}
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
if (!AllocateMemory(dec)) return 0;
InitIo(dec, io);
VP8DspInit(); // Init critical function pointers and look-up tables.
return 1;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Main reconstruction function. // Main reconstruction function.
@ -520,7 +535,7 @@ static const int kScan[16] = {
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
}; };
static inline int CheckMode(VP8Decoder* const dec, int mode) { static WEBP_INLINE int CheckMode(VP8Decoder* const dec, int mode) {
if (mode == B_DC_PRED) { if (mode == B_DC_PRED) {
if (dec->mb_x_ == 0) { if (dec->mb_x_ == 0) {
return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT; return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
@ -531,7 +546,7 @@ static inline int CheckMode(VP8Decoder* const dec, int mode) {
return mode; return mode;
} }
static inline void Copy32b(uint8_t* dst, uint8_t* src) { static WEBP_INLINE void Copy32b(uint8_t* dst, uint8_t* src) {
*(uint32_t*)dst = *(uint32_t*)src; *(uint32_t*)dst = *(uint32_t*)src;
} }

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -13,13 +13,16 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "webpi.h" #include "./webpi.h"
#include "vp8i.h" #include "./vp8i.h"
#include "../utils/utils.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
// In append mode, buffer allocations increase as multiples of this value.
// Needs to be a power of 2.
#define CHUNK_SIZE 4096 #define CHUNK_SIZE 4096
#define MAX_MB_SIZE 4096 #define MAX_MB_SIZE 4096
@ -33,6 +36,8 @@ typedef enum {
STATE_VP8_FRAME_HEADER, // For VP8 Frame header (within VP8 chunk). STATE_VP8_FRAME_HEADER, // For VP8 Frame header (within VP8 chunk).
STATE_VP8_PARTS0, STATE_VP8_PARTS0,
STATE_VP8_DATA, STATE_VP8_DATA,
STATE_VP8L_HEADER,
STATE_VP8L_DATA,
STATE_DONE, STATE_DONE,
STATE_ERROR STATE_ERROR
} DecState; } DecState;
@ -47,8 +52,8 @@ typedef enum {
// storage for partition #0 and partial data (in a rolling fashion) // storage for partition #0 and partial data (in a rolling fashion)
typedef struct { typedef struct {
MemBufferMode mode_; // Operation mode MemBufferMode mode_; // Operation mode
uint32_t start_; // start location of the data to be decoded size_t start_; // start location of the data to be decoded
uint32_t end_; // end location size_t end_; // end location
size_t buf_size_; // size of the allocated buffer size_t buf_size_; // size of the allocated buffer
uint8_t* buf_; // We don't own this buffer in case WebPIUpdate() uint8_t* buf_; // We don't own this buffer in case WebPIUpdate()
@ -59,12 +64,13 @@ typedef struct {
struct WebPIDecoder { struct WebPIDecoder {
DecState state_; // current decoding state DecState state_; // current decoding state
WebPDecParams params_; // Params to store output info WebPDecParams params_; // Params to store output info
VP8Decoder* dec_; int is_lossless_; // for down-casting 'dec_'.
void* dec_; // either a VP8Decoder or a VP8LDecoder instance
VP8Io io_; VP8Io io_;
MemBuffer mem_; // input memory buffer. MemBuffer mem_; // input memory buffer.
WebPDecBuffer output_; // output buffer (when no external one is supplied) WebPDecBuffer output_; // output buffer (when no external one is supplied)
uint32_t vp8_size_; // VP8 size extracted from VP8 Header. size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
}; };
// MB context to restore in case VP8DecodeMB() fails // MB context to restore in case VP8DecodeMB() fails
@ -80,102 +86,105 @@ typedef struct {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// MemBuffer: incoming data handling // MemBuffer: incoming data handling
#define REMAP(PTR, OLD_BASE, NEW_BASE) (PTR) = (NEW_BASE) + ((PTR) - OLD_BASE) static void RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) {
if (br->buf_ != NULL) {
br->buf_ += offset;
br->buf_end_ += offset;
}
}
static inline size_t MemDataSize(const MemBuffer* mem) { static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {
return (mem->end_ - mem->start_); return (mem->end_ - mem->start_);
} }
static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
MemBuffer* const mem = &idec->mem_;
const uint8_t* const new_base = mem->buf_ + mem->start_;
// note: for VP8, setting up idec->io_ is only really needed at the beginning
// of the decoding, till partition #0 is complete.
idec->io_.data = new_base;
idec->io_.data_size = MemDataSize(mem);
if (idec->dec_ != NULL) {
if (!idec->is_lossless_) {
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
const int last_part = dec->num_parts_ - 1;
if (offset != 0) {
int p;
for (p = 0; p <= last_part; ++p) {
RemapBitReader(dec->parts_ + p, offset);
}
// Remap partition #0 data pointer to new offset, but only in MAP
// mode (in APPEND mode, partition #0 is copied into a fixed memory).
if (mem->mode_ == MEM_MODE_MAP) {
RemapBitReader(&dec->br_, offset);
}
}
assert(last_part >= 0);
dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_;
} else { // Resize lossless bitreader
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem));
}
}
}
// Appends data to the end of MemBuffer->buf_. It expands the allocated memory // Appends data to the end of MemBuffer->buf_. It expands the allocated memory
// size if required and also updates VP8BitReader's if new memory is allocated. // size if required and also updates VP8BitReader's if new memory is allocated.
static int AppendToMemBuffer(WebPIDecoder* const idec, static int AppendToMemBuffer(WebPIDecoder* const idec,
const uint8_t* const data, size_t data_size) { const uint8_t* const data, size_t data_size) {
MemBuffer* const mem = &idec->mem_; MemBuffer* const mem = &idec->mem_;
VP8Decoder* const dec = idec->dec_; const uint8_t* const old_base = mem->buf_ + mem->start_;
const int last_part = dec->num_parts_ - 1;
assert(mem->mode_ == MEM_MODE_APPEND); assert(mem->mode_ == MEM_MODE_APPEND);
if (data_size > MAX_CHUNK_PAYLOAD) {
// security safeguard: trying to allocate more than what the format
// allows for a chunk should be considered a smoke smell.
return 0;
}
if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory
int p; const size_t current_size = MemDataSize(mem);
uint8_t* new_buf = NULL; const uint64_t new_size = (uint64_t)current_size + data_size;
const int num_chunks = (MemDataSize(mem) + data_size + CHUNK_SIZE - 1) const uint64_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1);
/ CHUNK_SIZE; uint8_t* const new_buf =
const size_t new_size = num_chunks * CHUNK_SIZE; (uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));
const uint8_t* const base = mem->buf_ + mem->start_; if (new_buf == NULL) return 0;
memcpy(new_buf, old_base, current_size);
new_buf = (uint8_t*)malloc(new_size);
if (!new_buf) return 0;
memcpy(new_buf, base, MemDataSize(mem));
// adjust VP8BitReader pointers
for (p = 0; p <= last_part; ++p) {
if (dec->parts_[p].buf_) {
REMAP(dec->parts_[p].buf_, base, new_buf);
REMAP(dec->parts_[p].buf_end_, base, new_buf);
}
}
// adjust memory pointers
free(mem->buf_); free(mem->buf_);
mem->buf_ = new_buf; mem->buf_ = new_buf;
mem->buf_size_ = new_size; mem->buf_size_ = (size_t)extra_size;
mem->end_ = MemDataSize(mem);
mem->start_ = 0; mem->start_ = 0;
mem->end_ = current_size;
} }
memcpy(mem->buf_ + mem->end_, data, data_size); memcpy(mem->buf_ + mem->end_, data, data_size);
mem->end_ += data_size; mem->end_ += data_size;
assert(mem->end_ <= mem->buf_size_); assert(mem->end_ <= mem->buf_size_);
dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_;
// note: setting up idec->io_ is only really needed at the beginning DoRemap(idec, mem->buf_ + mem->start_ - old_base);
// of the decoding, till partition #0 is complete.
idec->io_.data = mem->buf_ + mem->start_;
idec->io_.data_size = MemDataSize(mem);
return 1; return 1;
} }
static int RemapMemBuffer(WebPIDecoder* const idec, static int RemapMemBuffer(WebPIDecoder* const idec,
const uint8_t* const data, size_t data_size) { const uint8_t* const data, size_t data_size) {
int p;
MemBuffer* const mem = &idec->mem_; MemBuffer* const mem = &idec->mem_;
VP8Decoder* const dec = idec->dec_; const uint8_t* const old_base = mem->buf_ + mem->start_;
const int last_part = dec->num_parts_ - 1;
const uint8_t* base = mem->buf_;
assert(mem->mode_ == MEM_MODE_MAP); assert(mem->mode_ == MEM_MODE_MAP);
if (data_size < mem->buf_size_) {
return 0; // we cannot remap to a shorter buffer!
}
for (p = 0; p <= last_part; ++p) { if (data_size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
if (dec->parts_[p].buf_) {
REMAP(dec->parts_[p].buf_, base, data);
REMAP(dec->parts_[p].buf_end_, base, data);
}
}
dec->parts_[last_part].buf_end_ = data + data_size;
// Remap partition #0 data pointer to new offset.
if (dec->br_.buf_) {
REMAP(dec->br_.buf_, base, data);
REMAP(dec->br_.buf_end_, base, data);
}
mem->buf_ = (uint8_t*)data; mem->buf_ = (uint8_t*)data;
mem->end_ = mem->buf_size_ = data_size; mem->end_ = mem->buf_size_ = data_size;
idec->io_.data = data; DoRemap(idec, mem->buf_ + mem->start_ - old_base);
idec->io_.data_size = data_size;
return 1; return 1;
} }
static void InitMemBuffer(MemBuffer* const mem) { static void InitMemBuffer(MemBuffer* const mem) {
mem->mode_ = MEM_MODE_NONE; mem->mode_ = MEM_MODE_NONE;
mem->buf_ = 0; mem->buf_ = NULL;
mem->buf_size_ = 0; mem->buf_size_ = 0;
mem->part0_buf_ = 0; mem->part0_buf_ = NULL;
mem->part0_size_ = 0; mem->part0_size_ = 0;
} }
@ -197,8 +206,6 @@ static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
return 1; return 1;
} }
#undef REMAP
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Macroblock-decoding contexts // Macroblock-decoding contexts
@ -244,81 +251,111 @@ static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
} }
static void ChangeState(WebPIDecoder* const idec, DecState new_state, static void ChangeState(WebPIDecoder* const idec, DecState new_state,
uint32_t consumed_bytes) { size_t consumed_bytes) {
MemBuffer* const mem = &idec->mem_;
idec->state_ = new_state; idec->state_ = new_state;
idec->mem_.start_ += consumed_bytes; mem->start_ += consumed_bytes;
assert(idec->mem_.start_ <= idec->mem_.end_); assert(mem->start_ <= mem->end_);
idec->io_.data = mem->buf_ + mem->start_;
idec->io_.data_size = MemDataSize(mem);
} }
// Headers // Headers
static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) { static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_; MemBuffer* const mem = &idec->mem_;
uint32_t curr_size = MemDataSize(&idec->mem_); const uint8_t* data = mem->buf_ + mem->start_;
uint32_t vp8_size; size_t curr_size = MemDataSize(mem);
uint32_t bytes_skipped;
VP8StatusCode status; VP8StatusCode status;
WebPHeaderStructure headers;
status = WebPParseHeaders(&data, &curr_size, &vp8_size, &bytes_skipped); headers.data = data;
headers.data_size = curr_size;
status = WebPParseHeaders(&headers);
if (status == VP8_STATUS_NOT_ENOUGH_DATA) { if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet. return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.
} else if (status == VP8_STATUS_OK) { } else if (status != VP8_STATUS_OK) {
idec->vp8_size_ = vp8_size;
ChangeState(idec, STATE_VP8_FRAME_HEADER, bytes_skipped);
return VP8_STATUS_OK; // We have skipped all pre-VP8 chunks.
} else {
return IDecError(idec, status); return IDecError(idec, status);
} }
idec->chunk_size_ = headers.compressed_size;
idec->is_lossless_ = headers.is_lossless;
if (!idec->is_lossless_) {
VP8Decoder* const dec = VP8New();
if (dec == NULL) {
return VP8_STATUS_OUT_OF_MEMORY;
}
idec->dec_ = dec;
#ifdef WEBP_USE_THREAD
dec->use_threads_ = (idec->params_.options != NULL) &&
(idec->params_.options->use_threads > 0);
#else
dec->use_threads_ = 0;
#endif
dec->alpha_data_ = headers.alpha_data;
dec->alpha_data_size_ = headers.alpha_data_size;
ChangeState(idec, STATE_VP8_FRAME_HEADER, headers.offset);
} else {
VP8LDecoder* const dec = VP8LNew();
if (dec == NULL) {
return VP8_STATUS_OUT_OF_MEMORY;
}
idec->dec_ = dec;
ChangeState(idec, STATE_VP8L_HEADER, headers.offset);
}
return VP8_STATUS_OK;
} }
static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) { static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_; const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;
const uint32_t curr_size = MemDataSize(&idec->mem_); const size_t curr_size = MemDataSize(&idec->mem_);
uint32_t bits; uint32_t bits;
if (curr_size < VP8_FRAME_HEADER_SIZE) { if (curr_size < VP8_FRAME_HEADER_SIZE) {
// Not enough data bytes to extract VP8 Frame Header. // Not enough data bytes to extract VP8 Frame Header.
return VP8_STATUS_SUSPENDED; return VP8_STATUS_SUSPENDED;
} }
if (!VP8GetInfo(data, curr_size, idec->vp8_size_, NULL, NULL, NULL)) { if (!VP8GetInfo(data, curr_size, idec->chunk_size_, NULL, NULL)) {
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
} }
bits = data[0] | (data[1] << 8) | (data[2] << 16); bits = data[0] | (data[1] << 8) | (data[2] << 16);
idec->mem_.part0_size_ = (bits >> 5) + VP8_FRAME_HEADER_SIZE; idec->mem_.part0_size_ = (bits >> 5) + VP8_FRAME_HEADER_SIZE;
idec->io_.data_size = curr_size;
idec->io_.data = data; idec->io_.data = data;
idec->io_.data_size = curr_size;
idec->state_ = STATE_VP8_PARTS0; idec->state_ = STATE_VP8_PARTS0;
return VP8_STATUS_OK; return VP8_STATUS_OK;
} }
// Partition #0 // Partition #0
static int CopyParts0Data(WebPIDecoder* idec) { static int CopyParts0Data(WebPIDecoder* const idec) {
VP8BitReader* const br = &idec->dec_->br_; VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
VP8BitReader* const br = &dec->br_;
const size_t psize = br->buf_end_ - br->buf_; const size_t psize = br->buf_end_ - br->buf_;
MemBuffer* const mem = &idec->mem_; MemBuffer* const mem = &idec->mem_;
assert(!mem->part0_buf_); assert(!idec->is_lossless_);
assert(mem->part0_buf_ == NULL);
assert(psize > 0); assert(psize > 0);
assert(psize <= mem->part0_size_); assert(psize <= mem->part0_size_); // Format limit: no need for runtime check
if (mem->mode_ == MEM_MODE_APPEND) { if (mem->mode_ == MEM_MODE_APPEND) {
// We copy and grab ownership of the partition #0 data. // We copy and grab ownership of the partition #0 data.
uint8_t* const part0_buf = (uint8_t*)malloc(psize); uint8_t* const part0_buf = (uint8_t*)malloc(psize);
if (!part0_buf) { if (part0_buf == NULL) {
return 0; return 0;
} }
memcpy(part0_buf, br->buf_, psize); memcpy(part0_buf, br->buf_, psize);
mem->part0_buf_ = part0_buf; mem->part0_buf_ = part0_buf;
mem->start_ += psize;
br->buf_ = part0_buf; br->buf_ = part0_buf;
br->buf_end_ = part0_buf + psize; br->buf_end_ = part0_buf + psize;
} else { } else {
// Else: just keep pointers to the partition #0's data in dec_->br_. // Else: just keep pointers to the partition #0's data in dec_->br_.
} }
mem->start_ += psize;
return 1; return 1;
} }
static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) { static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
VP8Decoder* const dec = idec->dec_; VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
VP8Io* const io = &idec->io_; VP8Io* const io = &idec->io_;
const WebPDecParams* const params = &idec->params_; const WebPDecParams* const params = &idec->params_;
WebPDecBuffer* const output = params->output; WebPDecBuffer* const output = params->output;
@ -366,13 +403,11 @@ static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
// Remaining partitions // Remaining partitions
static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) { static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
VP8BitReader* br; VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
VP8Decoder* const dec = idec->dec_;
VP8Io* const io = &idec->io_; VP8Io* const io = &idec->io_;
assert(dec->ready_); assert(dec->ready_);
br = &dec->br_;
for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) { for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
VP8BitReader* token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)]; VP8BitReader* token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
if (dec->mb_x_ == 0) { if (dec->mb_x_ == 0) {
@ -415,13 +450,69 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
return VP8_STATUS_OK; return VP8_STATUS_OK;
} }
static int ErrorStatusLossless(WebPIDecoder* const idec, VP8StatusCode status) {
if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) {
return VP8_STATUS_SUSPENDED;
}
return IDecError(idec, status);
}
static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
VP8Io* const io = &idec->io_;
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
const WebPDecParams* const params = &idec->params_;
WebPDecBuffer* const output = params->output;
size_t curr_size = MemDataSize(&idec->mem_);
assert(idec->is_lossless_);
// Wait until there's enough data for decoding header.
if (curr_size < (idec->chunk_size_ >> 3)) {
return VP8_STATUS_SUSPENDED;
}
if (!VP8LDecodeHeader(dec, io)) {
return ErrorStatusLossless(idec, dec->status_);
}
// Allocate/verify output buffer now.
dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,
output);
if (dec->status_ != VP8_STATUS_OK) {
return IDecError(idec, dec->status_);
}
idec->state_ = STATE_VP8L_DATA;
return VP8_STATUS_OK;
}
static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
const size_t curr_size = MemDataSize(&idec->mem_);
assert(idec->is_lossless_);
// At present Lossless decoder can't decode image incrementally. So wait till
// all the image data is aggregated before image can be decoded.
if (curr_size < idec->chunk_size_) {
return VP8_STATUS_SUSPENDED;
}
if (!VP8LDecodeImage(dec)) {
return ErrorStatusLossless(idec, dec->status_);
}
idec->state_ = STATE_DONE;
return VP8_STATUS_OK;
}
// Main decoding loop // Main decoding loop
static VP8StatusCode IDecode(WebPIDecoder* idec) { static VP8StatusCode IDecode(WebPIDecoder* idec) {
VP8StatusCode status = VP8_STATUS_SUSPENDED; VP8StatusCode status = VP8_STATUS_SUSPENDED;
assert(idec->dec_);
if (idec->state_ == STATE_PRE_VP8) { if (idec->state_ == STATE_PRE_VP8) {
status = DecodeWebPHeaders(idec); status = DecodeWebPHeaders(idec);
} else {
if (idec->dec_ == NULL) {
return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
}
} }
if (idec->state_ == STATE_VP8_FRAME_HEADER) { if (idec->state_ == STATE_VP8_FRAME_HEADER) {
status = DecodeVP8FrameHeader(idec); status = DecodeVP8FrameHeader(idec);
@ -432,25 +523,26 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) {
if (idec->state_ == STATE_VP8_DATA) { if (idec->state_ == STATE_VP8_DATA) {
status = DecodeRemaining(idec); status = DecodeRemaining(idec);
} }
if (idec->state_ == STATE_VP8L_HEADER) {
status = DecodeVP8LHeader(idec);
}
if (idec->state_ == STATE_VP8L_DATA) {
status = DecodeVP8LData(idec);
}
return status; return status;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Public functions // Public functions
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* const output_buffer) { WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(WebPIDecoder)); WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(*idec));
if (idec == NULL) { if (idec == NULL) {
return NULL; return NULL;
} }
idec->dec_ = VP8New();
if (idec->dec_ == NULL) {
free(idec);
return NULL;
}
idec->state_ = STATE_PRE_VP8; idec->state_ = STATE_PRE_VP8;
idec->chunk_size_ = 0;
InitMemBuffer(&idec->mem_); InitMemBuffer(&idec->mem_);
WebPInitDecBuffer(&idec->output_); WebPInitDecBuffer(&idec->output_);
@ -460,19 +552,11 @@ WebPIDecoder* WebPINewDecoder(WebPDecBuffer* const output_buffer) {
idec->params_.output = output_buffer ? output_buffer : &idec->output_; idec->params_.output = output_buffer ? output_buffer : &idec->output_;
WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions. WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.
#ifdef WEBP_USE_THREAD
idec->dec_->use_threads_ = idec->params_.options &&
(idec->params_.options->use_threads > 0);
#else
idec->dec_->use_threads_ = 0;
#endif
idec->vp8_size_ = 0;
return idec; return idec;
} }
WebPIDecoder* WebPIDecode(const uint8_t* data, uint32_t data_size, WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
WebPDecoderConfig* const config) { WebPDecoderConfig* config) {
WebPIDecoder* idec; WebPIDecoder* idec;
// Parse the bitstream's features, if requested: // Parse the bitstream's features, if requested:
@ -483,7 +567,7 @@ WebPIDecoder* WebPIDecode(const uint8_t* data, uint32_t data_size,
} }
// Create an instance of the incremental decoder // Create an instance of the incremental decoder
idec = WebPINewDecoder(config ? &config->output : NULL); idec = WebPINewDecoder(config ? &config->output : NULL);
if (!idec) { if (idec == NULL) {
return NULL; return NULL;
} }
// Finish initialization // Finish initialization
@ -493,9 +577,15 @@ WebPIDecoder* WebPIDecode(const uint8_t* data, uint32_t data_size,
return idec; return idec;
} }
void WebPIDelete(WebPIDecoder* const idec) { void WebPIDelete(WebPIDecoder* idec) {
if (!idec) return; if (idec == NULL) return;
VP8Delete(idec->dec_); if (idec->dec_ != NULL) {
if (!idec->is_lossless_) {
VP8Delete(idec->dec_);
} else {
VP8LDelete(idec->dec_);
}
}
ClearMemBuffer(&idec->mem_); ClearMemBuffer(&idec->mem_);
WebPFreeDecBuffer(&idec->output_); WebPFreeDecBuffer(&idec->output_);
free(idec); free(idec);
@ -504,19 +594,12 @@ void WebPIDelete(WebPIDecoder* const idec) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Wrapper toward WebPINewDecoder // Wrapper toward WebPINewDecoder
WebPIDecoder* WebPINew(WEBP_CSP_MODE mode) {
WebPIDecoder* const idec = WebPINewDecoder(NULL);
if (!idec) return NULL;
idec->output_.colorspace = mode;
return idec;
}
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
int output_buffer_size, int output_stride) { size_t output_buffer_size, int output_stride) {
WebPIDecoder* idec; WebPIDecoder* idec;
if (mode >= MODE_YUV) return NULL; if (mode >= MODE_YUV) return NULL;
idec = WebPINewDecoder(NULL); idec = WebPINewDecoder(NULL);
if (!idec) return NULL; if (idec == NULL) return NULL;
idec->output_.colorspace = mode; idec->output_.colorspace = mode;
idec->output_.is_external_memory = 1; idec->output_.is_external_memory = 1;
idec->output_.u.RGBA.rgba = output_buffer; idec->output_.u.RGBA.rgba = output_buffer;
@ -525,12 +608,13 @@ WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
return idec; return idec;
} }
WebPIDecoder* WebPINewYUV(uint8_t* luma, int luma_size, int luma_stride, WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,
uint8_t* u, int u_size, int u_stride, uint8_t* u, size_t u_size, int u_stride,
uint8_t* v, int v_size, int v_stride) { uint8_t* v, size_t v_size, int v_stride,
uint8_t* a, size_t a_size, int a_stride) {
WebPIDecoder* const idec = WebPINewDecoder(NULL); WebPIDecoder* const idec = WebPINewDecoder(NULL);
if (!idec) return NULL; if (idec == NULL) return NULL;
idec->output_.colorspace = MODE_YUV; idec->output_.colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA;
idec->output_.is_external_memory = 1; idec->output_.is_external_memory = 1;
idec->output_.u.YUVA.y = luma; idec->output_.u.YUVA.y = luma;
idec->output_.u.YUVA.y_stride = luma_stride; idec->output_.u.YUVA.y_stride = luma_stride;
@ -541,16 +625,25 @@ WebPIDecoder* WebPINewYUV(uint8_t* luma, int luma_size, int luma_stride,
idec->output_.u.YUVA.v = v; idec->output_.u.YUVA.v = v;
idec->output_.u.YUVA.v_stride = v_stride; idec->output_.u.YUVA.v_stride = v_stride;
idec->output_.u.YUVA.v_size = v_size; idec->output_.u.YUVA.v_size = v_size;
idec->output_.u.YUVA.a = a;
idec->output_.u.YUVA.a_stride = a_stride;
idec->output_.u.YUVA.a_size = a_size;
return idec; return idec;
} }
WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride,
uint8_t* u, size_t u_size, int u_stride,
uint8_t* v, size_t v_size, int v_stride) {
return WebPINewYUVA(luma, luma_size, luma_stride,
u, u_size, u_stride,
v, v_size, v_stride,
NULL, 0, 0);
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) { static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
assert(idec); assert(idec);
if (idec->dec_ == NULL) {
return VP8_STATUS_USER_ABORT;
}
if (idec->state_ == STATE_ERROR) { if (idec->state_ == STATE_ERROR) {
return VP8_STATUS_BITSTREAM_ERROR; return VP8_STATUS_BITSTREAM_ERROR;
} }
@ -560,8 +653,8 @@ static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
return VP8_STATUS_SUSPENDED; return VP8_STATUS_SUSPENDED;
} }
VP8StatusCode WebPIAppend(WebPIDecoder* const idec, const uint8_t* data, VP8StatusCode WebPIAppend(WebPIDecoder* idec,
uint32_t data_size) { const uint8_t* data, size_t data_size) {
VP8StatusCode status; VP8StatusCode status;
if (idec == NULL || data == NULL) { if (idec == NULL || data == NULL) {
return VP8_STATUS_INVALID_PARAM; return VP8_STATUS_INVALID_PARAM;
@ -581,8 +674,8 @@ VP8StatusCode WebPIAppend(WebPIDecoder* const idec, const uint8_t* data,
return IDecode(idec); return IDecode(idec);
} }
VP8StatusCode WebPIUpdate(WebPIDecoder* const idec, const uint8_t* data, VP8StatusCode WebPIUpdate(WebPIDecoder* idec,
uint32_t data_size) { const uint8_t* data, size_t data_size) {
VP8StatusCode status; VP8StatusCode status;
if (idec == NULL || data == NULL) { if (idec == NULL || data == NULL) {
return VP8_STATUS_INVALID_PARAM; return VP8_STATUS_INVALID_PARAM;
@ -605,61 +698,67 @@ VP8StatusCode WebPIUpdate(WebPIDecoder* const idec, const uint8_t* data,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) { static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
if (!idec || !idec->dec_ || idec->state_ <= STATE_VP8_PARTS0) { if (idec == NULL || idec->dec_ == NULL) {
return NULL;
}
if (idec->state_ <= STATE_VP8_PARTS0) {
return NULL; return NULL;
} }
return idec->params_.output; return idec->params_.output;
} }
const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* const idec, const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
int* const left, int* const top, int* left, int* top,
int* const width, int* const height) { int* width, int* height) {
const WebPDecBuffer* const src = GetOutputBuffer(idec); const WebPDecBuffer* const src = GetOutputBuffer(idec);
if (left) *left = 0; if (left != NULL) *left = 0;
if (top) *top = 0; if (top != NULL) *top = 0;
// TODO(skal): later include handling of rotations. // TODO(skal): later include handling of rotations.
if (src) { if (src) {
if (width) *width = src->width; if (width != NULL) *width = src->width;
if (height) *height = idec->params_.last_y; if (height != NULL) *height = idec->params_.last_y;
} else { } else {
if (width) *width = 0; if (width != NULL) *width = 0;
if (height) *height = 0; if (height != NULL) *height = 0;
} }
return src; return src;
} }
uint8_t* WebPIDecGetRGB(const WebPIDecoder* const idec, int* last_y, uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
int* width, int* height, int* stride) { int* width, int* height, int* stride) {
const WebPDecBuffer* const src = GetOutputBuffer(idec); const WebPDecBuffer* const src = GetOutputBuffer(idec);
if (!src) return NULL; if (src == NULL) return NULL;
if (src->colorspace >= MODE_YUV) { if (src->colorspace >= MODE_YUV) {
return NULL; return NULL;
} }
if (last_y) *last_y = idec->params_.last_y; if (last_y != NULL) *last_y = idec->params_.last_y;
if (width) *width = src->width; if (width != NULL) *width = src->width;
if (height) *height = src->height; if (height != NULL) *height = src->height;
if (stride) *stride = src->u.RGBA.stride; if (stride != NULL) *stride = src->u.RGBA.stride;
return src->u.RGBA.rgba; return src->u.RGBA.rgba;
} }
uint8_t* WebPIDecGetYUV(const WebPIDecoder* const idec, int* last_y, uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
uint8_t** u, uint8_t** v, uint8_t** u, uint8_t** v, uint8_t** a,
int* width, int* height, int *stride, int* uv_stride) { int* width, int* height,
int* stride, int* uv_stride, int* a_stride) {
const WebPDecBuffer* const src = GetOutputBuffer(idec); const WebPDecBuffer* const src = GetOutputBuffer(idec);
if (!src) return NULL; if (src == NULL) return NULL;
if (src->colorspace < MODE_YUV) { if (src->colorspace < MODE_YUV) {
return NULL; return NULL;
} }
if (last_y) *last_y = idec->params_.last_y; if (last_y != NULL) *last_y = idec->params_.last_y;
if (u) *u = src->u.YUVA.u; if (u != NULL) *u = src->u.YUVA.u;
if (v) *v = src->u.YUVA.v; if (v != NULL) *v = src->u.YUVA.v;
if (width) *width = src->width; if (a != NULL) *a = src->u.YUVA.a;
if (height) *height = src->height; if (width != NULL) *width = src->width;
if (stride) *stride = src->u.YUVA.y_stride; if (height != NULL) *height = src->height;
if (uv_stride) *uv_stride = src->u.YUVA.u_stride; if (stride != NULL) *stride = src->u.YUVA.y_stride;
if (uv_stride != NULL) *uv_stride = src->u.YUVA.u_stride;
if (a_stride != NULL) *a_stride = src->u.YUVA.a_stride;
return src->u.YUVA.y; return src->u.YUVA.y;
} }
@ -669,7 +768,7 @@ int WebPISetIOHooks(WebPIDecoder* const idec,
VP8IoSetupHook setup, VP8IoSetupHook setup,
VP8IoTeardownHook teardown, VP8IoTeardownHook teardown,
void* user_data) { void* user_data) {
if (!idec || !idec->dec_ || idec->state_ > STATE_PRE_VP8) { if (idec == NULL || idec->state_ > STATE_PRE_VP8) {
return 0; return 0;
} }

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -32,11 +32,12 @@ static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
const int mb_w = io->mb_w; const int mb_w = io->mb_w;
const int mb_h = io->mb_h; const int mb_h = io->mb_h;
const int uv_w = (mb_w + 1) / 2; const int uv_w = (mb_w + 1) / 2;
const int uv_h = (mb_h + 1) / 2;
int j; int j;
for (j = 0; j < mb_h; ++j) { for (j = 0; j < mb_h; ++j) {
memcpy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w); memcpy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w);
} }
for (j = 0; j < (mb_h + 1) / 2; ++j) { for (j = 0; j < uv_h; ++j) {
memcpy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w); memcpy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w);
memcpy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w); memcpy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w);
} }
@ -103,16 +104,14 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
int num_lines_out = io->mb_h; // a priori guess int num_lines_out = io->mb_h; // a priori guess
const WebPRGBABuffer* const buf = &p->output->u.RGBA; const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* dst = buf->rgba + io->mb_y * buf->stride; uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
const WebPUpsampleLinePairFunc upsample = WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace];
io->a ? WebPUpsamplersKeepAlpha[p->output->colorspace]
: WebPUpsamplers[p->output->colorspace];
const uint8_t* cur_y = io->y; const uint8_t* cur_y = io->y;
const uint8_t* cur_u = io->u; const uint8_t* cur_u = io->u;
const uint8_t* cur_v = io->v; const uint8_t* cur_v = io->v;
const uint8_t* top_u = p->tmp_u; const uint8_t* top_u = p->tmp_u;
const uint8_t* top_v = p->tmp_v; const uint8_t* top_v = p->tmp_v;
int y = io->mb_y; int y = io->mb_y;
int y_end = io->mb_y + io->mb_h; const int y_end = io->mb_y + io->mb_h;
const int mb_w = io->mb_w; const int mb_w = io->mb_w;
const int uv_w = (mb_w + 1) / 2; const int uv_w = (mb_w + 1) / 2;
@ -121,11 +120,9 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w); upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w);
} else { } else {
// We can finish the left-over line from previous call. // We can finish the left-over line from previous call.
// Warning! Don't overwrite the alpha values (if any), as they
// are not lagging one line behind but are already written.
upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v, upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
dst - buf->stride, dst, mb_w); dst - buf->stride, dst, mb_w);
num_lines_out++; ++num_lines_out;
} }
// Loop over each output pairs of row. // Loop over each output pairs of row.
for (; y + 2 < y_end; y += 2) { for (; y + 2 < y_end; y += 2) {
@ -153,7 +150,7 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
// Process the very last row of even-sized picture // Process the very last row of even-sized picture
if (!(y_end & 1)) { if (!(y_end & 1)) {
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
dst + buf->stride, NULL, mb_w); dst + buf->stride, NULL, mb_w);
} }
} }
return num_lines_out; return num_lines_out;
@ -163,151 +160,130 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifdef WEBP_EXPERIMENTAL_FEATURES
static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) { static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
const uint8_t* alpha = io->a;
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
const int mb_w = io->mb_w; const int mb_w = io->mb_w;
const int mb_h = io->mb_h; const int mb_h = io->mb_h;
int j;
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
uint8_t* dst = buf->a + io->mb_y * buf->a_stride; uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
const uint8_t* alpha = io->a; int j;
if (alpha) {
if (alpha != NULL) {
for (j = 0; j < mb_h; ++j) { for (j = 0; j < mb_h; ++j) {
memcpy(dst, alpha, mb_w * sizeof(*dst)); memcpy(dst, alpha, mb_w * sizeof(*dst));
alpha += io->width; alpha += io->width;
dst += buf->a_stride; dst += buf->a_stride;
} }
} else if (buf->a != NULL) {
// the user requested alpha, but there is none, set it to opaque.
for (j = 0; j < mb_h; ++j) {
memset(dst, 0xff, mb_w * sizeof(*dst));
dst += buf->a_stride;
}
} }
return 0; return 0;
} }
static int GetAlphaSourceRow(const VP8Io* const io,
const uint8_t** alpha, int* const num_rows) {
int start_y = io->mb_y;
*num_rows = io->mb_h;
// Compensate for the 1-line delay of the fancy upscaler.
// This is similar to EmitFancyRGB().
if (io->fancy_upsampling) {
if (start_y == 0) {
// We don't process the last row yet. It'll be done during the next call.
--*num_rows;
} else {
--start_y;
// Fortunately, *alpha data is persistent, so we can go back
// one row and finish alpha blending, now that the fancy upscaler
// completed the YUV->RGB interpolation.
*alpha -= io->width;
}
if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) {
// If it's the very last call, we process all the remaining rows!
*num_rows = io->crop_bottom - io->crop_top - start_y;
}
}
return start_y;
}
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
const int mb_w = io->mb_w;
const int mb_h = io->mb_h;
int i, j;
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
const uint8_t* alpha = io->a; const uint8_t* alpha = io->a;
if (alpha) { if (alpha != NULL) {
for (j = 0; j < mb_h; ++j) { const int mb_w = io->mb_w;
const WEBP_CSP_MODE colorspace = p->output->colorspace;
const int alpha_first =
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
int num_rows;
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
uint32_t alpha_mask = 0xff;
int i, j;
for (j = 0; j < num_rows; ++j) {
for (i = 0; i < mb_w; ++i) { for (i = 0; i < mb_w; ++i) {
dst[4 * i + 3] = alpha[i]; const uint32_t alpha_value = alpha[i];
dst[4 * i] = alpha_value;
alpha_mask &= alpha_value;
} }
alpha += io->width; alpha += io->width;
dst += buf->stride; dst += buf->stride;
} }
// alpha_mask is < 0xff if there's non-trivial alpha to premultiply with.
if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) {
WebPApplyAlphaMultiply(base_rgba, alpha_first,
mb_w, num_rows, buf->stride);
}
} }
return 0; return 0;
} }
#endif /* WEBP_EXPERIMENTAL_FEATURES */ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
const uint8_t* alpha = io->a;
if (alpha != NULL) {
const int mb_w = io->mb_w;
const WEBP_CSP_MODE colorspace = p->output->colorspace;
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
int num_rows;
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
uint8_t* alpha_dst = base_rgba + 1;
uint32_t alpha_mask = 0x0f;
int i, j;
//------------------------------------------------------------------------------ for (j = 0; j < num_rows; ++j) {
// Simple picture rescaler for (i = 0; i < mb_w; ++i) {
// Fill in the alpha value (converted to 4 bits).
// TODO(skal): start a common library for encoder and decoder, and factorize const uint32_t alpha_value = alpha[i] >> 4;
// this code in. alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
alpha_mask &= alpha_value;
#define RFIX 30
#define MULT(x,y) (((int64_t)(x) * (y) + (1 << (RFIX - 1))) >> RFIX)
static void InitRescaler(WebPRescaler* const wrk,
int src_width, int src_height,
uint8_t* dst,
int dst_width, int dst_height, int dst_stride,
int x_add, int x_sub, int y_add, int y_sub,
int32_t* work) {
wrk->x_expand = (src_width < dst_width);
wrk->src_width = src_width;
wrk->src_height = src_height;
wrk->dst_width = dst_width;
wrk->dst_height = dst_height;
wrk->dst = dst;
wrk->dst_stride = dst_stride;
// for 'x_expand', we use bilinear interpolation
wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add - x_sub;
wrk->x_sub = wrk->x_expand ? (x_add - 1) : x_sub;
wrk->y_accum = y_add;
wrk->y_add = y_add;
wrk->y_sub = y_sub;
wrk->fx_scale = (1 << RFIX) / x_sub;
wrk->fy_scale = (1 << RFIX) / y_sub;
wrk->fxy_scale = wrk->x_expand ?
((int64_t)dst_height << RFIX) / (x_sub * src_height) :
((int64_t)dst_height << RFIX) / (x_add * src_height);
wrk->irow = work;
wrk->frow = work + dst_width;
}
static inline void ImportRow(const uint8_t* const src,
WebPRescaler* const wrk) {
int x_in = 0;
int x_out;
int accum = 0;
if (!wrk->x_expand) {
int sum = 0;
for (x_out = 0; x_out < wrk->dst_width; ++x_out) {
accum += wrk->x_add;
for (; accum > 0; accum -= wrk->x_sub) {
sum += src[x_in++];
}
{ // Emit next horizontal pixel.
const int32_t base = src[x_in++];
const int32_t frac = base * (-accum);
wrk->frow[x_out] = (sum + base) * wrk->x_sub - frac;
// fresh fractional start for next pixel
sum = MULT(frac, wrk->fx_scale);
} }
alpha += io->width;
alpha_dst += buf->stride;
} }
} else { // simple bilinear interpolation if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
int left = src[0], right = src[0]; WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
for (x_out = 0; x_out < wrk->dst_width; ++x_out) {
if (accum < 0) {
left = right;
right = src[++x_in];
accum += wrk->x_add;
}
wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
accum -= wrk->x_sub;
} }
} }
// Accumulate the new row's contribution return 0;
for (x_out = 0; x_out < wrk->dst_width; ++x_out) {
wrk->irow[x_out] += wrk->frow[x_out];
}
} }
static void ExportRow(WebPRescaler* const wrk) {
int x_out;
const int yscale = wrk->fy_scale * (-wrk->y_accum);
assert(wrk->y_accum <= 0);
for (x_out = 0; x_out < wrk->dst_width; ++x_out) {
const int frac = MULT(wrk->frow[x_out], yscale);
const int v = (int)MULT(wrk->irow[x_out] - frac, wrk->fxy_scale);
wrk->dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
wrk->irow[x_out] = frac; // new fractional start
}
wrk->y_accum += wrk->y_add;
wrk->dst += wrk->dst_stride;
}
#undef MULT
#undef RFIX
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// YUV rescaling (no final RGB conversion needed) // YUV rescaling (no final RGB conversion needed)
static int Rescale(const uint8_t* src, int src_stride, static int Rescale(const uint8_t* src, int src_stride,
int new_lines, WebPRescaler* const wrk) { int new_lines, WebPRescaler* const wrk) {
int num_lines_out = 0; int num_lines_out = 0;
while (new_lines-- > 0) { // import new contribution of one source row. while (new_lines > 0) { // import new contributions of source rows.
ImportRow(src, wrk); const int lines_in = WebPRescalerImport(wrk, new_lines, src, src_stride);
src += src_stride; src += lines_in * src_stride;
wrk->y_accum -= wrk->y_sub; new_lines -= lines_in;
while (wrk->y_accum <= 0) { // emit output row(s) num_lines_out += WebPRescalerExport(wrk); // emit output row(s)
ExportRow(wrk);
num_lines_out++;
}
} }
return num_lines_out; return num_lines_out;
} }
@ -322,19 +298,14 @@ static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
} }
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) { static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
if (io->a) { if (io->a != NULL) {
Rescale(io->a, io->width, io->mb_h, &p->scaler_a); Rescale(io->a, io->width, io->mb_h, &p->scaler_a);
} }
return 0; return 0;
} }
static int IsAlphaMode(WEBP_CSP_MODE mode) {
return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB ||
mode == MODE_RGBA_4444 || mode == MODE_YUVA);
}
static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) { static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
const int has_alpha = IsAlphaMode(p->output->colorspace); const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
const WebPYUVABuffer* const buf = &p->output->u.YUVA; const WebPYUVABuffer* const buf = &p->output->u.YUVA;
const int out_width = io->scaled_width; const int out_width = io->scaled_width;
const int out_height = io->scaled_height; const int out_height = io->scaled_height;
@ -356,26 +327,27 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
return 0; // memory error return 0; // memory error
} }
work = (int32_t*)p->memory; work = (int32_t*)p->memory;
InitRescaler(&p->scaler_y, io->mb_w, io->mb_h, WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
buf->y, out_width, out_height, buf->y_stride, buf->y, out_width, out_height, buf->y_stride, 1,
io->mb_w, out_width, io->mb_h, out_height, io->mb_w, out_width, io->mb_h, out_height,
work); work);
InitRescaler(&p->scaler_u, uv_in_width, uv_in_height, WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
buf->u, uv_out_width, uv_out_height, buf->u_stride, buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
uv_in_width, uv_out_width, uv_in_width, uv_out_width,
uv_in_height, uv_out_height, uv_in_height, uv_out_height,
work + work_size); work + work_size);
InitRescaler(&p->scaler_v, uv_in_width, uv_in_height, WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
buf->v, uv_out_width, uv_out_height, buf->v_stride, buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
uv_in_width, uv_out_width, uv_in_width, uv_out_width,
uv_in_height, uv_out_height, uv_in_height, uv_out_height,
work + work_size + uv_work_size); work + work_size + uv_work_size);
p->emit = EmitRescaledYUV; p->emit = EmitRescaledYUV;
if (has_alpha) { if (has_alpha) {
InitRescaler(&p->scaler_a, io->mb_w, io->mb_h, WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
buf->a, out_width, out_height, buf->a_stride, buf->a, out_width, out_height, buf->a_stride, 1,
io->mb_w, out_width, io->mb_h, out_height, io->mb_w, out_width, io->mb_h, out_height,
work + work_size + 2 * uv_work_size); work + work_size + 2 * uv_work_size);
p->emit_alpha = EmitRescaledAlphaYUV; p->emit_alpha = EmitRescaledAlphaYUV;
} }
return 1; return 1;
@ -384,20 +356,6 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// RGBA rescaling // RGBA rescaling
// import new contributions until one row is ready to be output, or all input
// is consumed.
static int Import(const uint8_t* src, int src_stride,
int new_lines, WebPRescaler* const wrk) {
int num_lines_in = 0;
while (num_lines_in < new_lines && wrk->y_accum > 0) {
ImportRow(src, wrk);
src += src_stride;
++num_lines_in;
wrk->y_accum -= wrk->y_sub;
}
return num_lines_in;
}
static int ExportRGB(WebPDecParams* const p, int y_pos) { static int ExportRGB(WebPDecParams* const p, int y_pos) {
const WebPYUV444Converter convert = const WebPYUV444Converter convert =
WebPYUV444Converters[p->output->colorspace]; WebPYUV444Converters[p->output->colorspace];
@ -406,16 +364,17 @@ static int ExportRGB(WebPDecParams* const p, int y_pos) {
int num_lines_out = 0; int num_lines_out = 0;
// For RGB rescaling, because of the YUV420, current scan position // For RGB rescaling, because of the YUV420, current scan position
// U/V can be +1/-1 line from the Y one. Hence the double test. // U/V can be +1/-1 line from the Y one. Hence the double test.
while (p->scaler_y.y_accum <= 0 && p->scaler_u.y_accum <= 0) { while (WebPRescalerHasPendingOutput(&p->scaler_y) &&
WebPRescalerHasPendingOutput(&p->scaler_u)) {
assert(p->last_y + y_pos + num_lines_out < p->output->height); assert(p->last_y + y_pos + num_lines_out < p->output->height);
assert(p->scaler_u.y_accum == p->scaler_v.y_accum); assert(p->scaler_u.y_accum == p->scaler_v.y_accum);
ExportRow(&p->scaler_y); WebPRescalerExportRow(&p->scaler_y);
ExportRow(&p->scaler_u); WebPRescalerExportRow(&p->scaler_u);
ExportRow(&p->scaler_v); WebPRescalerExportRow(&p->scaler_v);
convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst, convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst,
dst, p->scaler_y.dst_width); dst, p->scaler_y.dst_width);
dst += buf->stride; dst += buf->stride;
num_lines_out++; ++num_lines_out;
} }
return num_lines_out; return num_lines_out;
} }
@ -426,12 +385,15 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
int j = 0, uv_j = 0; int j = 0, uv_j = 0;
int num_lines_out = 0; int num_lines_out = 0;
while (j < mb_h) { while (j < mb_h) {
const int y_lines_in = Import(io->y + j * io->y_stride, io->y_stride, const int y_lines_in =
mb_h - j, &p->scaler_y); WebPRescalerImport(&p->scaler_y, mb_h - j,
const int u_lines_in = Import(io->u + uv_j * io->uv_stride, io->uv_stride, io->y + j * io->y_stride, io->y_stride);
uv_mb_h - uv_j, &p->scaler_u); const int u_lines_in =
const int v_lines_in = Import(io->v + uv_j * io->uv_stride, io->uv_stride, WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j,
uv_mb_h - uv_j, &p->scaler_v); io->u + uv_j * io->uv_stride, io->uv_stride);
const int v_lines_in =
WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j,
io->v + uv_j * io->uv_stride, io->uv_stride);
(void)v_lines_in; // remove a gcc warning (void)v_lines_in; // remove a gcc warning
assert(u_lines_in == v_lines_in); assert(u_lines_in == v_lines_in);
j += y_lines_in; j += y_lines_in;
@ -443,34 +405,80 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
static int ExportAlpha(WebPDecParams* const p, int y_pos) { static int ExportAlpha(WebPDecParams* const p, int y_pos) {
const WebPRGBABuffer* const buf = &p->output->u.RGBA; const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride; uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
const WEBP_CSP_MODE colorspace = p->output->colorspace;
const int alpha_first =
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
int num_lines_out = 0; int num_lines_out = 0;
while (p->scaler_a.y_accum <= 0) { const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
uint32_t alpha_mask = 0xff;
const int width = p->scaler_a.dst_width;
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
int i; int i;
assert(p->last_y + y_pos + num_lines_out < p->output->height); assert(p->last_y + y_pos + num_lines_out < p->output->height);
ExportRow(&p->scaler_a); WebPRescalerExportRow(&p->scaler_a);
for (i = 0; i < p->scaler_a.dst_width; ++i) { for (i = 0; i < width; ++i) {
dst[4 * i + 3] = p->scaler_a.dst[i]; const uint32_t alpha_value = p->scaler_a.dst[i];
dst[4 * i] = alpha_value;
alpha_mask &= alpha_value;
} }
dst += buf->stride; dst += buf->stride;
num_lines_out++; ++num_lines_out;
}
if (is_premult_alpha && alpha_mask != 0xff) {
WebPApplyAlphaMultiply(base_rgba, alpha_first,
width, num_lines_out, buf->stride);
}
return num_lines_out;
}
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
uint8_t* alpha_dst = base_rgba + 1;
int num_lines_out = 0;
const WEBP_CSP_MODE colorspace = p->output->colorspace;
const int width = p->scaler_a.dst_width;
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
uint32_t alpha_mask = 0x0f;
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
int i;
assert(p->last_y + y_pos + num_lines_out < p->output->height);
WebPRescalerExportRow(&p->scaler_a);
for (i = 0; i < width; ++i) {
// Fill in the alpha value (converted to 4 bits).
const uint32_t alpha_value = p->scaler_a.dst[i] >> 4;
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
alpha_mask &= alpha_value;
}
alpha_dst += buf->stride;
++num_lines_out;
}
if (is_premult_alpha && alpha_mask != 0x0f) {
WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
} }
return num_lines_out; return num_lines_out;
} }
static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
if (io->a) { if (io->a != NULL) {
int j = 0, pos = 0; WebPRescaler* const scaler = &p->scaler_a;
int j = 0;
int pos = 0;
while (j < io->mb_h) { while (j < io->mb_h) {
j += Import(io->a + j * io->width, io->width, io->mb_h - j, &p->scaler_a); j += WebPRescalerImport(scaler, io->mb_h - j,
pos += ExportAlpha(p, pos); io->a + j * io->width, io->width);
pos += p->emit_alpha_row(p, pos);
} }
} }
return 0; return 0;
} }
static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) { static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
const int has_alpha = IsAlphaMode(p->output->colorspace); const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
const int out_width = io->scaled_width; const int out_width = io->scaled_width;
const int out_height = io->scaled_height; const int out_height = io->scaled_height;
const int uv_in_width = (io->mb_w + 1) >> 1; const int uv_in_width = (io->mb_w + 1) >> 1;
@ -486,33 +494,38 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
tmp_size1 += work_size; tmp_size1 += work_size;
tmp_size2 += out_width; tmp_size2 += out_width;
} }
p->memory = p->memory = calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
if (p->memory == NULL) { if (p->memory == NULL) {
return 0; // memory error return 0; // memory error
} }
work = (int32_t*)p->memory; work = (int32_t*)p->memory;
tmp = (uint8_t*)(work + tmp_size1); tmp = (uint8_t*)(work + tmp_size1);
InitRescaler(&p->scaler_y, io->mb_w, io->mb_h, WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
tmp + 0 * out_width, out_width, out_height, 0, tmp + 0 * out_width, out_width, out_height, 0, 1,
io->mb_w, out_width, io->mb_h, out_height, io->mb_w, out_width, io->mb_h, out_height,
work + 0 * work_size); work + 0 * work_size);
InitRescaler(&p->scaler_u, uv_in_width, uv_in_height, WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
tmp + 1 * out_width, out_width, out_height, 0, tmp + 1 * out_width, out_width, out_height, 0, 1,
io->mb_w, 2 * out_width, io->mb_h, 2 * out_height, io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
work + 1 * work_size); work + 1 * work_size);
InitRescaler(&p->scaler_v, uv_in_width, uv_in_height, WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
tmp + 2 * out_width, out_width, out_height, 0, tmp + 2 * out_width, out_width, out_height, 0, 1,
io->mb_w, 2 * out_width, io->mb_h, 2 * out_height, io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
work + 2 * work_size); work + 2 * work_size);
p->emit = EmitRescaledRGB; p->emit = EmitRescaledRGB;
if (has_alpha) { if (has_alpha) {
InitRescaler(&p->scaler_a, io->mb_w, io->mb_h, WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
tmp + 3 * out_width, out_width, out_height, 0, tmp + 3 * out_width, out_width, out_height, 0, 1,
io->mb_w, out_width, io->mb_h, out_height, io->mb_w, out_width, io->mb_h, out_height,
work + 3 * work_size); work + 3 * work_size);
p->emit_alpha = EmitRescaledAlphaRGB; p->emit_alpha = EmitRescaledAlphaRGB;
if (p->output->colorspace == MODE_RGBA_4444 ||
p->output->colorspace == MODE_rgbA_4444) {
p->emit_alpha_row = ExportAlphaRGBA4444;
} else {
p->emit_alpha_row = ExportAlpha;
}
} }
return 1; return 1;
} }
@ -520,67 +533,17 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Default custom functions // Default custom functions
// Setup crop_xxx fields, mb_w and mb_h
static int InitFromOptions(const WebPDecoderOptions* const options,
VP8Io* const io) {
const int W = io->width;
const int H = io->height;
int x = 0, y = 0, w = W, h = H;
// Cropping
io->use_cropping = (options != NULL) && (options->use_cropping > 0);
if (io->use_cropping) {
w = options->crop_width;
h = options->crop_height;
// TODO(skal): take colorspace into account. Don't assume YUV420.
x = options->crop_left & ~1;
y = options->crop_top & ~1;
if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
return 0; // out of frame boundary error
}
}
io->crop_left = x;
io->crop_top = y;
io->crop_right = x + w;
io->crop_bottom = y + h;
io->mb_w = w;
io->mb_h = h;
// Scaling
io->use_scaling = (options != NULL) && (options->use_scaling > 0);
if (io->use_scaling) {
if (options->scaled_width <= 0 || options->scaled_height <= 0) {
return 0;
}
io->scaled_width = options->scaled_width;
io->scaled_height = options->scaled_height;
}
// Filter
io->bypass_filtering = options && options->bypass_filtering;
// Fancy upsampler
#ifdef FANCY_UPSAMPLING
io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling);
#endif
if (io->use_scaling) {
// disable filter (only for large downscaling ratio).
io->bypass_filtering = (io->scaled_width < W * 3 / 4) &&
(io->scaled_height < H * 3 / 4);
io->fancy_upsampling = 0;
}
return 1;
}
static int CustomSetup(VP8Io* io) { static int CustomSetup(VP8Io* io) {
WebPDecParams* const p = (WebPDecParams*)io->opaque; WebPDecParams* const p = (WebPDecParams*)io->opaque;
const int is_rgb = (p->output->colorspace < MODE_YUV); const WEBP_CSP_MODE colorspace = p->output->colorspace;
const int is_rgb = WebPIsRGBMode(colorspace);
const int is_alpha = WebPIsAlphaMode(colorspace);
p->memory = NULL; p->memory = NULL;
p->emit = NULL; p->emit = NULL;
p->emit_alpha = NULL; p->emit_alpha = NULL;
if (!InitFromOptions(p->options, io)) { p->emit_alpha_row = NULL;
if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) {
return 0; return 0;
} }
@ -609,12 +572,14 @@ static int CustomSetup(VP8Io* io) {
} else { } else {
p->emit = EmitYUV; p->emit = EmitYUV;
} }
#ifdef WEBP_EXPERIMENTAL_FEATURES if (is_alpha) { // need transparency output
if (IsAlphaMode(p->output->colorspace)) { if (WebPIsPremultipliedMode(colorspace)) WebPInitPremultiply();
// We need transparency output p->emit_alpha =
p->emit_alpha = is_rgb ? EmitAlphaRGB : EmitAlphaYUV; (colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
EmitAlphaRGBA4444
: is_rgb ? EmitAlphaRGB
: EmitAlphaYUV;
} }
#endif
} }
if (is_rgb) { if (is_rgb) {
@ -626,7 +591,7 @@ static int CustomSetup(VP8Io* io) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static int CustomPut(const VP8Io* io) { static int CustomPut(const VP8Io* io) {
WebPDecParams* p = (WebPDecParams*)io->opaque; WebPDecParams* const p = (WebPDecParams*)io->opaque;
const int mb_w = io->mb_w; const int mb_w = io->mb_w;
const int mb_h = io->mb_h; const int mb_h = io->mb_h;
int num_lines_out; int num_lines_out;

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -11,7 +11,8 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include "vp8i.h"
#include "./vp8i.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,13 +9,13 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include "vp8i.h" #include "./vp8i.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
static inline int clip(int v, int M) { static WEBP_INLINE int clip(int v, int M) {
return v < 0 ? 0 : v > M ? M : v; return v < 0 ? 0 : v > M ? M : v;
} }
@ -94,8 +94,10 @@ void VP8ParseQuant(VP8Decoder* const dec) {
m->y1_mat_[1] = kAcTable[clip(q + 0, 127)]; m->y1_mat_[1] = kAcTable[clip(q + 0, 127)];
m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2; m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2;
// TODO(skal): make it another table? // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16.
m->y2_mat_[1] = kAcTable[clip(q + dqy2_ac, 127)] * 155 / 100; // The smallest precision for that is '(x*6349) >> 12' but 16 is a good
// word size.
m->y2_mat_[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16;
if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8; if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8;
m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)]; m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)];

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -59,8 +59,8 @@ static const int8_t kMVRef[8] = {
}; };
static const int8_t kMVRef4[6] = { static const int8_t kMVRef4[6] = {
-LEFT4, 1 -LEFT4, 1,
-ABOVE4, 2 -ABOVE4, 2,
-ZERO4, -NEW4 -ZERO4, -NEW4
}; };
#endif #endif

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -10,8 +10,11 @@
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h> #include <stdlib.h>
#include "vp8i.h"
#include "webpi.h" #include "./vp8i.h"
#include "./vp8li.h"
#include "./webpi.h"
#include "../utils/bit_reader.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -32,20 +35,22 @@ static void SetOk(VP8Decoder* const dec) {
} }
int VP8InitIoInternal(VP8Io* const io, int version) { int VP8InitIoInternal(VP8Io* const io, int version) {
if (version != WEBP_DECODER_ABI_VERSION) if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
return 0; // mismatch error return 0; // mismatch error
if (io) { }
if (io != NULL) {
memset(io, 0, sizeof(*io)); memset(io, 0, sizeof(*io));
} }
return 1; return 1;
} }
VP8Decoder* VP8New(void) { VP8Decoder* VP8New(void) {
VP8Decoder* dec = (VP8Decoder*)calloc(1, sizeof(VP8Decoder)); VP8Decoder* const dec = (VP8Decoder*)calloc(1, sizeof(*dec));
if (dec) { if (dec != NULL) {
SetOk(dec); SetOk(dec);
WebPWorkerInit(&dec->worker_); WebPWorkerInit(&dec->worker_);
dec->ready_ = 0; dec->ready_ = 0;
dec->num_parts_ = 1;
} }
return dec; return dec;
} }
@ -56,35 +61,46 @@ VP8StatusCode VP8Status(VP8Decoder* const dec) {
} }
const char* VP8StatusMessage(VP8Decoder* const dec) { const char* VP8StatusMessage(VP8Decoder* const dec) {
if (!dec) return "no object"; if (dec == NULL) return "no object";
if (!dec->error_msg_) return "OK"; if (!dec->error_msg_) return "OK";
return dec->error_msg_; return dec->error_msg_;
} }
void VP8Delete(VP8Decoder* const dec) { void VP8Delete(VP8Decoder* const dec) {
if (dec) { if (dec != NULL) {
VP8Clear(dec); VP8Clear(dec);
free(dec); free(dec);
} }
} }
int VP8SetError(VP8Decoder* const dec, int VP8SetError(VP8Decoder* const dec,
VP8StatusCode error, const char * const msg) { VP8StatusCode error, const char* const msg) {
dec->status_ = error; // TODO This check would be unnecessary if alpha decompression was separated
dec->error_msg_ = msg; // from VP8ProcessRow/FinishRow. This avoids setting 'dec->status_' to
dec->ready_ = 0; // something other than VP8_STATUS_BITSTREAM_ERROR on alpha decompression
// failure.
if (dec->status_ == VP8_STATUS_OK) {
dec->status_ = error;
dec->error_msg_ = msg;
dec->ready_ = 0;
}
return 0; return 0;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int VP8GetInfo(const uint8_t* data, uint32_t data_size, uint32_t chunk_size, int VP8CheckSignature(const uint8_t* const data, size_t data_size) {
int* width, int* height, int* has_alpha) { return (data_size >= 3 &&
if (data_size < 10) { data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a);
}
int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
int* const width, int* const height) {
if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) {
return 0; // not enough data return 0; // not enough data
} }
// check signature // check signature
if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) { if (!VP8CheckSignature(data + 3, data_size - 3)) {
return 0; // Wrong signature. return 0; // Wrong signature.
} else { } else {
const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16); const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16);
@ -92,14 +108,6 @@ int VP8GetInfo(const uint8_t* data, uint32_t data_size, uint32_t chunk_size,
const int w = ((data[7] << 8) | data[6]) & 0x3fff; const int w = ((data[7] << 8) | data[6]) & 0x3fff;
const int h = ((data[9] << 8) | data[8]) & 0x3fff; const int h = ((data[9] << 8) | data[8]) & 0x3fff;
if (has_alpha) {
#ifdef WEBP_EXPERIMENTAL_FEATURES
if (data_size < 11) return 0;
*has_alpha = !!(data[10] & 0x80); // the colorspace_ bit
#else
*has_alpha = 0;
#endif
}
if (!key_frame) { // Not a keyframe. if (!key_frame) { // Not a keyframe.
return 0; return 0;
} }
@ -129,7 +137,7 @@ int VP8GetInfo(const uint8_t* data, uint32_t data_size, uint32_t chunk_size,
// Header parsing // Header parsing
static void ResetSegmentHeader(VP8SegmentHeader* const hdr) { static void ResetSegmentHeader(VP8SegmentHeader* const hdr) {
assert(hdr); assert(hdr != NULL);
hdr->use_segment_ = 0; hdr->use_segment_ = 0;
hdr->update_map_ = 0; hdr->update_map_ = 0;
hdr->absolute_delta_ = 1; hdr->absolute_delta_ = 1;
@ -140,8 +148,8 @@ static void ResetSegmentHeader(VP8SegmentHeader* const hdr) {
// Paragraph 9.3 // Paragraph 9.3
static int ParseSegmentHeader(VP8BitReader* br, static int ParseSegmentHeader(VP8BitReader* br,
VP8SegmentHeader* hdr, VP8Proba* proba) { VP8SegmentHeader* hdr, VP8Proba* proba) {
assert(br); assert(br != NULL);
assert(hdr); assert(hdr != NULL);
hdr->use_segment_ = VP8Get(br); hdr->use_segment_ = VP8Get(br);
if (hdr->use_segment_) { if (hdr->use_segment_) {
hdr->update_map_ = VP8Get(br); hdr->update_map_ = VP8Get(br);
@ -177,7 +185,7 @@ static int ParseSegmentHeader(VP8BitReader* br,
// is returned, and this is an unrecoverable error. // is returned, and this is an unrecoverable error.
// If the partitions were positioned ok, VP8_STATUS_OK is returned. // If the partitions were positioned ok, VP8_STATUS_OK is returned.
static VP8StatusCode ParsePartitions(VP8Decoder* const dec, static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
const uint8_t* buf, uint32_t size) { const uint8_t* buf, size_t size) {
VP8BitReader* const br = &dec->br_; VP8BitReader* const br = &dec->br_;
const uint8_t* sz = buf; const uint8_t* sz = buf;
const uint8_t* buf_end = buf + size; const uint8_t* buf_end = buf + size;
@ -248,13 +256,12 @@ static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
// Topmost call // Topmost call
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
const uint8_t* buf; const uint8_t* buf;
uint32_t buf_size; size_t buf_size;
uint32_t vp8_chunk_size;
uint32_t bytes_skipped;
VP8FrameHeader* frm_hdr; VP8FrameHeader* frm_hdr;
VP8PictureHeader* pic_hdr; VP8PictureHeader* pic_hdr;
VP8BitReader* br; VP8BitReader* br;
VP8StatusCode status; VP8StatusCode status;
WebPHeaderStructure headers;
if (dec == NULL) { if (dec == NULL) {
return 0; return 0;
@ -265,16 +272,32 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
"null VP8Io passed to VP8GetHeaders()"); "null VP8Io passed to VP8GetHeaders()");
} }
buf = io->data;
buf_size = io->data_size;
// Process Pre-VP8 chunks. // Process Pre-VP8 chunks.
status = WebPParseHeaders(&buf, &buf_size, &vp8_chunk_size, &bytes_skipped); headers.data = io->data;
headers.data_size = io->data_size;
status = WebPParseHeaders(&headers);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return VP8SetError(dec, status, "Incorrect/incomplete header."); return VP8SetError(dec, status, "Incorrect/incomplete header.");
} }
if (headers.is_lossless) {
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
"Unexpected lossless format encountered.");
}
if (dec->alpha_data_ == NULL) {
assert(dec->alpha_data_size_ == 0);
// We have NOT set alpha data yet. Set it now.
// (This is to ensure that dec->alpha_data_ is NOT reset to NULL if
// WebPParseHeaders() is called more than once, as in incremental decoding
// case.)
dec->alpha_data_ = headers.alpha_data;
dec->alpha_data_size_ = headers.alpha_data_size;
}
// Process the VP8 frame header. // Process the VP8 frame header.
buf = headers.data + headers.offset;
buf_size = headers.data_size - headers.offset;
assert(headers.data_size >= headers.offset); // WebPParseHeaders' guarantee
if (buf_size < 4) { if (buf_size < 4) {
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
"Truncated header."); "Truncated header.");
@ -305,7 +328,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
"cannot parse picture header"); "cannot parse picture header");
} }
if (buf[0] != 0x9d || buf[1] != 0x01 || buf[2] != 0x2a) { if (!VP8CheckSignature(buf, buf_size)) {
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
"Bad code word"); "Bad code word");
} }
@ -342,9 +365,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
"bad partition length"); "bad partition length");
} }
dec->alpha_data_ = NULL;
dec->alpha_data_size_ = 0;
br = &dec->br_; br = &dec->br_;
VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_); VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_);
buf += frm_hdr->partition_length_; buf += frm_hdr->partition_length_;
@ -418,17 +438,9 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
if (frm_hdr->partition_length_ < kTrailerSize || if (frm_hdr->partition_length_ < kTrailerSize ||
ext_buf[kTrailerSize - 1] != kTrailerMarker) { ext_buf[kTrailerSize - 1] != kTrailerMarker) {
Error:
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
"RIFF: Inconsistent extra information."); "RIFF: Inconsistent extra information.");
} }
// Alpha
size = (ext_buf[4] << 0) | (ext_buf[5] << 8) | (ext_buf[6] << 16);
if (frm_hdr->partition_length_ < size + kTrailerSize) {
goto Error;
}
dec->alpha_data_ = (size > 0) ? ext_buf - size : NULL;
dec->alpha_data_size_ = size;
// Layer // Layer
size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16); size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16);
@ -466,8 +478,9 @@ typedef const uint8_t (*ProbaArray)[NUM_CTX][NUM_PROBAS]; // for const-casting
// Returns the position of the last non-zero coeff plus one // Returns the position of the last non-zero coeff plus one
// (and 0 if there's no coeff at all) // (and 0 if there's no coeff at all)
static int GetCoeffs(VP8BitReader* const br, ProbaArray prob, static int GetCoeffs(VP8BitReader* const br, ProbaArray prob,
int ctx, const uint16_t dq[2], int n, int16_t* out) { int ctx, const quant_t dq, int n, int16_t* out) {
const uint8_t* p = prob[kBands[n]][ctx]; // n is either 0 or 1 here. kBands[n] is not necessary for extracting '*p'.
const uint8_t* p = prob[n][ctx];
if (!VP8GetBit(br, p[0])) { // first EOB is more a 'CBP' bit. if (!VP8GetBit(br, p[0])) { // first EOB is more a 'CBP' bit.
return 0; return 0;
} }
@ -556,6 +569,7 @@ static void ParseResiduals(VP8Decoder* const dec,
uint32_t non_zero_dc = 0; uint32_t non_zero_dc = 0;
int x, y, ch; int x, y, ch;
nz_dc.i32 = nz_ac.i32 = 0;
memset(dst, 0, 384 * sizeof(*dst)); memset(dst, 0, 384 * sizeof(*dst));
if (!dec->is_i4x4_) { // parse DC if (!dec->is_i4x4_) { // parse DC
int16_t dc[16] = { 0 }; int16_t dc[16] = { 0 };
@ -747,7 +761,7 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
} }
dec->ready_ = 0; dec->ready_ = 0;
return 1; return ok;
} }
void VP8Clear(VP8Decoder* const dec) { void VP8Clear(VP8Decoder* const dec) {

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -13,6 +13,7 @@
#define WEBP_DEC_VP8I_H_ #define WEBP_DEC_VP8I_H_
#include <string.h> // for memcpy() #include <string.h> // for memcpy()
#include "./vp8li.h"
#include "../utils/bit_reader.h" #include "../utils/bit_reader.h"
#include "../utils/thread.h" #include "../utils/thread.h"
#include "../dsp/dsp.h" #include "../dsp/dsp.h"
@ -26,8 +27,8 @@ extern "C" {
// version numbers // version numbers
#define DEC_MAJ_VERSION 0 #define DEC_MAJ_VERSION 0
#define DEC_MIN_VERSION 1 #define DEC_MIN_VERSION 2
#define DEC_REV_VERSION 3 #define DEC_REV_VERSION 0
#define ONLY_KEYFRAME_CODE // to remove any code related to P-Frames #define ONLY_KEYFRAME_CODE // to remove any code related to P-Frames
@ -162,8 +163,9 @@ typedef struct { // used for syntax-parsing
} VP8MB; } VP8MB;
// Dequantization matrices // Dequantization matrices
typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower).
typedef struct { typedef struct {
uint16_t y1_mat_[2], y2_mat_[2], uv_mat_[2]; // [DC / AC] quant_t y1_mat_, y2_mat_, uv_mat_;
} VP8QuantMatrix; } VP8QuantMatrix;
// Persistent information needed by the parallel processing // Persistent information needed by the parallel processing
@ -250,7 +252,7 @@ struct VP8Decoder {
// main memory chunk for the above data. Persistent. // main memory chunk for the above data. Persistent.
void* mem_; void* mem_;
int mem_size_; size_t mem_size_;
// Per macroblock non-persistent infos. // Per macroblock non-persistent infos.
int mb_x_, mb_y_; // current position, in macroblock units int mb_x_, mb_y_; // current position, in macroblock units
@ -274,7 +276,7 @@ struct VP8Decoder {
// extensions // extensions
const uint8_t* alpha_data_; // compressed alpha data (if present) const uint8_t* alpha_data_; // compressed alpha data (if present)
size_t alpha_data_size_; size_t alpha_data_size_;
uint8_t* alpha_plane_; // output uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
int layer_colorspace_; int layer_colorspace_;
const uint8_t* layer_data_; // compressed layer data (if present) const uint8_t* layer_data_; // compressed layer data (if present)
@ -286,15 +288,7 @@ struct VP8Decoder {
// in vp8.c // in vp8.c
int VP8SetError(VP8Decoder* const dec, int VP8SetError(VP8Decoder* const dec,
VP8StatusCode error, const char * const msg); VP8StatusCode error, const char* const msg);
// Validates the VP8 data-header and retrieve basic header information viz width
// and height. Returns 0 in case of formatting error. *width/*height/*has_alpha
// can be passed NULL.
int VP8GetInfo(const uint8_t* data,
uint32_t data_size, // data available so far
uint32_t chunk_size, // total data size expect in the chunk
int *width, int *height, int *has_alpha);
// in tree.c // in tree.c
void VP8ResetProba(VP8Proba* const proba); void VP8ResetProba(VP8Proba* const proba);
@ -316,14 +310,10 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io);
// Must always be called in pair with VP8EnterCritical(). // Must always be called in pair with VP8EnterCritical().
// Returns false in case of error. // Returns false in case of error.
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io); int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
// Filter the decoded macroblock row (if needed)
int VP8FinishRow(VP8Decoder* const dec, VP8Io* io); // multi threaded call
// Process the last decoded row (filtering + output) // Process the last decoded row (filtering + output)
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io); int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
// Store a block, along with filtering params // Store a block, along with filtering params
void VP8StoreBlock(VP8Decoder* const dec); void VP8StoreBlock(VP8Decoder* const dec);
// Finalize and transmit a complete row. Return false in case of user-abort.
int VP8FinishRow(VP8Decoder* const dec, VP8Io* const io);
// To be called at the start of a new scanline, to initialize predictors. // To be called at the start of a new scanline, to initialize predictors.
void VP8InitScanline(VP8Decoder* const dec); void VP8InitScanline(VP8Decoder* const dec);
// Decode one macroblock. Returns false if there is not enough data. // Decode one macroblock. Returns false if there is not enough data.

1200
src/dec/vp8l.c Normal file

File diff suppressed because it is too large Load Diff

121
src/dec/vp8li.h Normal file
View File

@ -0,0 +1,121 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Lossless decoder: internal header.
//
// Author: Skal (pascal.massimino@gmail.com)
// Vikas Arora(vikaas.arora@gmail.com)
#ifndef WEBP_DEC_VP8LI_H_
#define WEBP_DEC_VP8LI_H_
#include <string.h> // for memcpy()
#include "./webpi.h"
#include "../utils/bit_reader.h"
#include "../utils/color_cache.h"
#include "../utils/huffman.h"
#include "../webp/format_constants.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
typedef enum {
READ_DATA = 0,
READ_HDR = 1,
READ_DIM = 2
} VP8LDecodeState;
typedef struct VP8LTransform VP8LTransform;
struct VP8LTransform {
VP8LImageTransformType type_; // transform type.
int bits_; // subsampling bits defining transform window.
int xsize_; // transform window X index.
int ysize_; // transform window Y index.
uint32_t *data_; // transform data.
};
typedef struct {
HuffmanTree htrees_[HUFFMAN_CODES_PER_META_CODE];
} HTreeGroup;
typedef struct {
int color_cache_size_;
VP8LColorCache color_cache_;
int huffman_mask_;
int huffman_subsample_bits_;
int huffman_xsize_;
uint32_t *huffman_image_;
int num_htree_groups_;
HTreeGroup *htree_groups_;
} VP8LMetadata;
typedef struct {
VP8StatusCode status_;
VP8LDecodeState action_;
VP8LDecodeState state_;
VP8Io *io_;
const WebPDecBuffer *output_; // shortcut to io->opaque->output
uint32_t *argb_; // Internal data: always in BGRA color mode.
uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
VP8LBitReader br_;
int width_;
int height_;
int last_row_; // last input row decoded so far.
int last_out_row_; // last row output so far.
VP8LMetadata hdr_;
int next_transform_;
VP8LTransform transforms_[NUM_TRANSFORMS];
// or'd bitset storing the transforms types.
uint32_t transforms_seen_;
uint8_t *rescaler_memory; // Working memory for rescaling work.
WebPRescaler *rescaler; // Common rescaler for all channels.
} VP8LDecoder;
//------------------------------------------------------------------------------
// internal functions. Not public.
// in vp8l.c
// Decodes a raw image stream (without header) and store the alpha data
// into *output, which must be of size width x height. Returns false in case
// of error.
int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data,
size_t data_size, uint8_t* const output);
// Allocates and initialize a new lossless decoder instance.
VP8LDecoder* VP8LNew(void);
// Decodes the image header. Returns false in case of error.
int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io);
// Decodes an image. It's required to decode the lossless header before calling
// this function. Returns false in case of error, with updated dec->status_.
int VP8LDecodeImage(VP8LDecoder* const dec);
// Resets the decoder in its initial state, reclaiming memory.
// Preserves the dec->status_ value.
void VP8LClear(VP8LDecoder* const dec);
// Clears and deallocate a lossless decoder instance.
void VP8LDelete(VP8LDecoder* const dec);
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
#endif /* WEBP_DEC_VP8LI_H_ */

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -10,8 +10,11 @@
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h> #include <stdlib.h>
#include "vp8i.h"
#include "webpi.h" #include "./vp8i.h"
#include "./vp8li.h"
#include "./webpi.h"
#include "../webp/format_constants.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -24,105 +27,148 @@ extern "C" {
// 4...7 size of image data (including metadata) starting at offset 8 // 4...7 size of image data (including metadata) starting at offset 8
// 8...11 "WEBP" our form-type signature // 8...11 "WEBP" our form-type signature
// The RIFF container (12 bytes) is followed by appropriate chunks: // The RIFF container (12 bytes) is followed by appropriate chunks:
// 12..15 "VP8 ": 4-bytes tags, describing the raw video format used // 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format
// 16..19 size of the raw VP8 image data, starting at offset 20 // 16..19 size of the raw VP8 image data, starting at offset 20
// 20.... the VP8 bytes // 20.... the VP8 bytes
// Or, // Or,
// 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format
// 16..19 size of the raw VP8L image data, starting at offset 20
// 20.... the VP8L bytes
// Or,
// 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk. // 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk.
// 16..19 size of the VP8X chunk starting at offset 20. // 16..19 size of the VP8X chunk starting at offset 20.
// 20..23 VP8X flags bit-map corresponding to the chunk-types present. // 20..23 VP8X flags bit-map corresponding to the chunk-types present.
// 24..27 Width of the Canvas Image. // 24..26 Width of the Canvas Image.
// 28..31 Height of the Canvas Image. // 27..29 Height of the Canvas Image.
// There can be extra chunks after the "VP8X" chunk (ICCP, TILE, FRM, VP8, // There can be extra chunks after the "VP8X" chunk (ICCP, TILE, FRM, VP8,
// META ...) // META ...)
// All 32-bits sizes are in little-endian order. // All sizes are in little-endian order.
// Note: chunk data must be padded to multiple of 2 in size // Note: chunk data size must be padded to multiple of 2 when written.
static inline uint32_t get_le32(const uint8_t* const data) { static WEBP_INLINE uint32_t get_le24(const uint8_t* const data) {
return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); return data[0] | (data[1] << 8) | (data[2] << 16);
} }
VP8StatusCode WebPParseRIFF(const uint8_t** data, uint32_t* data_size, static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) {
uint32_t* riff_size) { return (uint32_t)get_le24(data) | (data[3] << 24);
assert(data); }
assert(data_size);
assert(riff_size);
if (*data_size >= RIFF_HEADER_SIZE && // Validates the RIFF container (if detected) and skips over it.
!memcmp(*data, "RIFF", TAG_SIZE)) { // If a RIFF container is detected,
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and
// VP8_STATUS_OK otherwise.
// In case there are not enough bytes (partial RIFF container), return 0 for
// *riff_size. Else return the RIFF size extracted from the header.
static VP8StatusCode ParseRIFF(const uint8_t** const data,
size_t* const data_size,
size_t* const riff_size) {
assert(data != NULL);
assert(data_size != NULL);
assert(riff_size != NULL);
*riff_size = 0; // Default: no RIFF present.
if (*data_size >= RIFF_HEADER_SIZE && !memcmp(*data, "RIFF", TAG_SIZE)) {
if (memcmp(*data + 8, "WEBP", TAG_SIZE)) { if (memcmp(*data + 8, "WEBP", TAG_SIZE)) {
return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature. return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature.
} else { } else {
*riff_size = get_le32(*data + TAG_SIZE); const uint32_t size = get_le32(*data + TAG_SIZE);
// Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn"). // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
if (*riff_size < TAG_SIZE + CHUNK_HEADER_SIZE) { if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
return VP8_STATUS_BITSTREAM_ERROR; return VP8_STATUS_BITSTREAM_ERROR;
} }
// We have a RIFF container. Skip it. // We have a RIFF container. Skip it.
*riff_size = size;
*data += RIFF_HEADER_SIZE; *data += RIFF_HEADER_SIZE;
*data_size -= RIFF_HEADER_SIZE; *data_size -= RIFF_HEADER_SIZE;
} }
} else {
*riff_size = 0; // Did not get full RIFF Header.
} }
return VP8_STATUS_OK; return VP8_STATUS_OK;
} }
VP8StatusCode WebPParseVP8X(const uint8_t** data, uint32_t* data_size, // Validates the VP8X header and skips over it.
uint32_t* bytes_skipped, // Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header,
int* width, int* height, uint32_t* flags) { // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
assert(data); // VP8_STATUS_OK otherwise.
assert(data_size); // If a VP8X chunk is found, found_vp8x is set to true and *width_ptr,
assert(bytes_skipped); // *height_ptr and *flags_ptr are set to the corresponding values extracted
// from the VP8X chunk.
static VP8StatusCode ParseVP8X(const uint8_t** const data,
size_t* const data_size,
int* const found_vp8x,
int* const width_ptr, int* const height_ptr,
uint32_t* const flags_ptr) {
const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
assert(data != NULL);
assert(data_size != NULL);
assert(found_vp8x != NULL);
*bytes_skipped = 0; *found_vp8x = 0;
if (*data_size < CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE) { if (*data_size < CHUNK_HEADER_SIZE) {
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
} }
if (!memcmp(*data, "VP8X", TAG_SIZE)) { if (!memcmp(*data, "VP8X", TAG_SIZE)) {
int width, height;
uint32_t flags;
const uint32_t chunk_size = get_le32(*data + TAG_SIZE); const uint32_t chunk_size = get_le32(*data + TAG_SIZE);
if (chunk_size != VP8X_CHUNK_SIZE) { if (chunk_size != VP8X_CHUNK_SIZE) {
return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size. return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size.
} }
if (flags) {
*flags = get_le32(*data + 8); // Verify if enough data is available to validate the VP8X chunk.
if (*data_size < vp8x_size) {
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
} }
if (width) { flags = get_le32(*data + 8);
*width = get_le32(*data + 12); width = 1 + get_le24(*data + 12);
height = 1 + get_le24(*data + 15);
if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
return VP8_STATUS_BITSTREAM_ERROR; // image is too large
} }
if (height) {
*height = get_le32(*data + 16); if (flags_ptr != NULL) *flags_ptr = flags;
} if (width_ptr != NULL) *width_ptr = width;
// We have consumed 20 bytes from VP8X. Skip them. if (height_ptr != NULL) *height_ptr = height;
*bytes_skipped = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; // Skip over VP8X header bytes.
*data += *bytes_skipped; *data += vp8x_size;
*data_size -= *bytes_skipped; *data_size -= vp8x_size;
*found_vp8x = 1;
} }
return VP8_STATUS_OK; return VP8_STATUS_OK;
} }
VP8StatusCode WebPParseOptionalChunks(const uint8_t** data, uint32_t* data_size, // Skips to the next VP8/VP8L chunk header in the data given the size of the
uint32_t riff_size, // RIFF chunk 'riff_size'.
uint32_t* bytes_skipped) { // Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered,
// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
// VP8_STATUS_OK otherwise.
// If an alpha chunk is found, *alpha_data and *alpha_size are set
// appropriately.
static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
size_t* const data_size,
size_t const riff_size,
const uint8_t** const alpha_data,
size_t* const alpha_size) {
const uint8_t* buf; const uint8_t* buf;
uint32_t buf_size; size_t buf_size;
uint32_t total_size = TAG_SIZE + // "WEBP".
assert(data); CHUNK_HEADER_SIZE + // "VP8Xnnnn".
assert(data_size); VP8X_CHUNK_SIZE; // data.
assert(bytes_skipped); assert(data != NULL);
assert(data_size != NULL);
buf = *data; buf = *data;
buf_size = *data_size; buf_size = *data_size;
*bytes_skipped = 0;
assert(alpha_data != NULL);
assert(alpha_size != NULL);
*alpha_data = NULL;
*alpha_size = 0;
while (1) { while (1) {
uint32_t chunk_size; uint32_t chunk_size;
uint32_t cur_skip_size; uint32_t disk_chunk_size; // chunk_size with padding
const uint32_t bytes_skipped_header = TAG_SIZE + // "WEBP".
CHUNK_HEADER_SIZE + // "VP8Xnnnn".
VP8X_CHUNK_SIZE; // Data.
*data = buf; *data = buf;
*data_size = buf_size; *data_size = buf_size;
@ -131,119 +177,197 @@ VP8StatusCode WebPParseOptionalChunks(const uint8_t** data, uint32_t* data_size,
} }
chunk_size = get_le32(buf + TAG_SIZE); chunk_size = get_le32(buf + TAG_SIZE);
cur_skip_size = CHUNK_HEADER_SIZE + chunk_size; // For odd-sized chunk-payload, there's one byte padding at the end.
disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1;
total_size += disk_chunk_size;
// Check that total bytes skipped along with current chunk size // Check that total bytes skipped so far does not exceed riff_size.
// does not exceed riff_size. if (riff_size > 0 && (total_size > riff_size)) {
if (riff_size > 0 && return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
(bytes_skipped_header + *bytes_skipped + cur_skip_size > riff_size)) {
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
} }
if (buf_size < cur_skip_size) { // Insufficient data. if (buf_size < disk_chunk_size) { // Insufficient data.
return VP8_STATUS_NOT_ENOUGH_DATA; return VP8_STATUS_NOT_ENOUGH_DATA;
} }
if (!memcmp(buf, "VP8 ", TAG_SIZE)) { // A valid VP8 header. if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header.
*alpha_data = buf + CHUNK_HEADER_SIZE;
*alpha_size = chunk_size;
} else if (!memcmp(buf, "VP8 ", TAG_SIZE) ||
!memcmp(buf, "VP8L", TAG_SIZE)) { // A valid VP8/VP8L header.
return VP8_STATUS_OK; // Found. return VP8_STATUS_OK; // Found.
} }
// We have a full & valid chunk; skip it. // We have a full and valid chunk; skip it.
buf += cur_skip_size; buf += disk_chunk_size;
buf_size -= cur_skip_size; buf_size -= disk_chunk_size;
*bytes_skipped += cur_skip_size;
} }
} }
VP8StatusCode WebPParseVP8Header(const uint8_t** data, uint32_t* data_size, // Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it.
uint32_t riff_size, uint32_t* bytes_skipped, // Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than
uint32_t* vp8_chunk_size) { // riff_size) VP8/VP8L header,
assert(data); // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
assert(data_size); // VP8_STATUS_OK otherwise.
assert(bytes_skipped); // If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes
assert(vp8_chunk_size); // extracted from the VP8/VP8L chunk header.
// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
*bytes_skipped = 0; static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
*vp8_chunk_size = 0; size_t* const data_size,
size_t riff_size,
size_t* const chunk_size,
int* const is_lossless) {
const uint8_t* const data = *data_ptr;
const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE);
const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE);
const uint32_t minimal_size =
TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR
// "WEBP" + "VP8Lnnnn"
assert(data != NULL);
assert(data_size != NULL);
assert(chunk_size != NULL);
assert(is_lossless != NULL);
if (*data_size < CHUNK_HEADER_SIZE) { if (*data_size < CHUNK_HEADER_SIZE) {
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
} }
if (!memcmp(*data, "VP8 ", TAG_SIZE)) { if (is_vp8 || is_vp8l) {
*vp8_chunk_size = get_le32(*data + TAG_SIZE); // Bitstream contains VP8/VP8L header.
if (riff_size >= TAG_SIZE + CHUNK_HEADER_SIZE && // "WEBP" + "VP8 nnnn". const uint32_t size = get_le32(data + TAG_SIZE);
(*vp8_chunk_size > riff_size - (TAG_SIZE + CHUNK_HEADER_SIZE))) { if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information.
} }
// We have consumed CHUNK_HEADER_SIZE bytes from VP8 Header. Skip them. // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
*bytes_skipped = CHUNK_HEADER_SIZE; *chunk_size = size;
*data += *bytes_skipped; *data_ptr += CHUNK_HEADER_SIZE;
*data_size -= *bytes_skipped; *data_size -= CHUNK_HEADER_SIZE;
*is_lossless = is_vp8l;
} else {
// Raw VP8/VP8L bitstream (no header).
*is_lossless = VP8LCheckSignature(data, *data_size);
*chunk_size = *data_size;
} }
return VP8_STATUS_OK; return VP8_STATUS_OK;
} }
VP8StatusCode WebPParseHeaders(const uint8_t** data, uint32_t* data_size, //------------------------------------------------------------------------------
uint32_t* vp8_size, uint32_t* bytes_skipped) {
const uint8_t* buf; // Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
uint32_t buf_size; // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
uint32_t riff_size; // minimal amount will be read to fetch the remaining parameters.
uint32_t vp8_size_tmp; // If 'headers' is non-NULL this function will attempt to locate both alpha
uint32_t optional_data_size; // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
uint32_t vp8x_skip_size; // Note: The following chunk sequences (before the raw VP8/VP8L data) are
uint32_t vp8_skip_size; // considered valid by this function:
// RIFF + VP8(L)
// RIFF + VP8X + (optional chunks) + VP8(L)
// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
size_t data_size,
int* const width,
int* const height,
int* const has_alpha,
WebPHeaderStructure* const headers) {
int found_riff = 0;
int found_vp8x = 0;
VP8StatusCode status; VP8StatusCode status;
WebPHeaderStructure hdrs;
assert(data); if (data == NULL || data_size < RIFF_HEADER_SIZE) {
assert(data_size);
assert(vp8_size);
assert(bytes_skipped);
buf = *data;
buf_size = *data_size;
*vp8_size = 0;
*bytes_skipped = 0;
if (buf == NULL || buf_size < RIFF_HEADER_SIZE) {
return VP8_STATUS_NOT_ENOUGH_DATA; return VP8_STATUS_NOT_ENOUGH_DATA;
} }
memset(&hdrs, 0, sizeof(hdrs));
hdrs.data = data;
hdrs.data_size = data_size;
// Skip over RIFF header. // Skip over RIFF header.
if (WebPParseRIFF(&buf, &buf_size, &riff_size) != VP8_STATUS_OK) { status = ParseRIFF(&data, &data_size, &hdrs.riff_size);
return VP8_STATUS_BITSTREAM_ERROR; // Wrong RIFF Header.
}
// Skip over VP8X header.
status = WebPParseVP8X(&buf, &buf_size, &vp8x_skip_size, NULL, NULL, NULL);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return status; // Wrong VP8X Chunk / Insufficient data. return status; // Wrong RIFF header / insufficient data.
} }
if (vp8x_skip_size > 0) { found_riff = (hdrs.riff_size > 0);
// Skip over optional chunks.
status = WebPParseOptionalChunks(&buf, &buf_size, riff_size, // Skip over VP8X.
&optional_data_size); {
uint32_t flags = 0;
status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return status; // Found an invalid chunk size / Insufficient data. return status; // Wrong VP8X / insufficient data.
}
if (!found_riff && found_vp8x) {
// Note: This restriction may be removed in the future, if it becomes
// necessary to send VP8X chunk to the decoder.
return VP8_STATUS_BITSTREAM_ERROR;
}
if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG_BIT);
if (found_vp8x && headers == NULL) {
return VP8_STATUS_OK; // Return features from VP8X header.
} }
} }
// Skip over VP8 chunk header. if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA;
status = WebPParseVP8Header(&buf, &buf_size, riff_size, &vp8_skip_size,
&vp8_size_tmp); // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
if (status != VP8_STATUS_OK) { if ((found_riff && found_vp8x) ||
return status; // Invalid VP8 header / Insufficient data. (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) {
} status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
if (vp8_skip_size > 0) { &hdrs.alpha_data, &hdrs.alpha_data_size);
*vp8_size = vp8_size_tmp; if (status != VP8_STATUS_OK) {
return status; // Found an invalid chunk size / insufficient data.
}
} }
*bytes_skipped = buf - *data; // Skip over VP8/VP8L header.
assert(*bytes_skipped == *data_size - buf_size); status = ParseVP8Header(&data, &data_size, hdrs.riff_size,
*data = buf; &hdrs.compressed_size, &hdrs.is_lossless);
*data_size = buf_size; if (status != VP8_STATUS_OK) {
return VP8_STATUS_OK; return status; // Wrong VP8/VP8L chunk-header / insufficient data.
}
if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
return VP8_STATUS_BITSTREAM_ERROR;
}
if (!hdrs.is_lossless) {
if (data_size < VP8_FRAME_HEADER_SIZE) {
return VP8_STATUS_NOT_ENOUGH_DATA;
}
// Validates raw VP8 data.
if (!VP8GetInfo(data, data_size,
(uint32_t)hdrs.compressed_size, width, height)) {
return VP8_STATUS_BITSTREAM_ERROR;
}
} else {
if (data_size < VP8L_FRAME_HEADER_SIZE) {
return VP8_STATUS_NOT_ENOUGH_DATA;
}
// Validates raw VP8L data.
if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) {
return VP8_STATUS_BITSTREAM_ERROR;
}
}
if (has_alpha != NULL) {
// If the data did not contain a VP8X/VP8L chunk the only definitive way
// to set this is by looking for alpha data (from an ALPH chunk).
*has_alpha |= (hdrs.alpha_data != NULL);
}
if (headers != NULL) {
*headers = hdrs;
headers->offset = data - headers->data;
assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
assert(headers->offset == headers->data_size - data_size);
}
return VP8_STATUS_OK; // Return features from VP8 header.
}
VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
assert(headers != NULL);
// fill out headers, ignore width/height/has_alpha.
return ParseHeadersInternal(headers->data, headers->data_size,
NULL, NULL, NULL, headers);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -259,43 +383,72 @@ void WebPResetDecParams(WebPDecParams* const params) {
// "Into" decoding variants // "Into" decoding variants
// Main flow // Main flow
static VP8StatusCode DecodeInto(const uint8_t* data, uint32_t data_size, static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
WebPDecParams* const params) { WebPDecParams* const params) {
VP8Decoder* dec = VP8New(); VP8StatusCode status;
VP8StatusCode status = VP8_STATUS_OK;
VP8Io io; VP8Io io;
WebPHeaderStructure headers;
assert(params); headers.data = data;
if (dec == NULL) { headers.data_size = data_size;
return VP8_STATUS_INVALID_PARAM; status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks.
if (status != VP8_STATUS_OK) {
return status;
} }
assert(params != NULL);
VP8InitIo(&io); VP8InitIo(&io);
io.data = data; io.data = headers.data + headers.offset;
io.data_size = data_size; io.data_size = headers.data_size - headers.offset;
WebPInitCustomIo(params, &io); // Plug the I/O functions. WebPInitCustomIo(params, &io); // Plug the I/O functions.
if (!headers.is_lossless) {
VP8Decoder* const dec = VP8New();
if (dec == NULL) {
return VP8_STATUS_OUT_OF_MEMORY;
}
#ifdef WEBP_USE_THREAD #ifdef WEBP_USE_THREAD
dec->use_threads_ = params->options && (params->options->use_threads > 0); dec->use_threads_ = params->options && (params->options->use_threads > 0);
#else #else
dec->use_threads_ = 0; dec->use_threads_ = 0;
#endif #endif
dec->alpha_data_ = headers.alpha_data;
dec->alpha_data_size_ = headers.alpha_data_size;
// Decode bitstream header, update io->width/io->height. // Decode bitstream header, update io->width/io->height.
if (!VP8GetHeaders(dec, &io)) { if (!VP8GetHeaders(dec, &io)) {
status = VP8_STATUS_BITSTREAM_ERROR; status = dec->status_; // An error occurred. Grab error status.
} else { } else {
// Allocate/check output buffers. // Allocate/check output buffers.
status = WebPAllocateDecBuffer(io.width, io.height, params->options, status = WebPAllocateDecBuffer(io.width, io.height, params->options,
params->output); params->output);
if (status == VP8_STATUS_OK) { if (status == VP8_STATUS_OK) { // Decode
// Decode if (!VP8Decode(dec, &io)) {
if (!VP8Decode(dec, &io)) { status = dec->status_;
status = dec->status_; }
} }
} }
VP8Delete(dec);
} else {
VP8LDecoder* const dec = VP8LNew();
if (dec == NULL) {
return VP8_STATUS_OUT_OF_MEMORY;
}
if (!VP8LDecodeHeader(dec, &io)) {
status = dec->status_; // An error occurred. Grab error status.
} else {
// Allocate/check output buffers.
status = WebPAllocateDecBuffer(io.width, io.height, params->options,
params->output);
if (status == VP8_STATUS_OK) { // Decode
if (!VP8LDecodeImage(dec)) {
status = dec->status_;
}
}
}
VP8LDelete(dec);
} }
VP8Delete(dec);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
WebPFreeDecBuffer(params->output); WebPFreeDecBuffer(params->output);
} }
@ -304,8 +457,10 @@ static VP8StatusCode DecodeInto(const uint8_t* data, uint32_t data_size,
// Helpers // Helpers
static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace, static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
const uint8_t* data, uint32_t data_size, const uint8_t* const data,
uint8_t* rgba, int stride, int size) { size_t data_size,
uint8_t* const rgba,
int stride, size_t size) {
WebPDecParams params; WebPDecParams params;
WebPDecBuffer buf; WebPDecBuffer buf;
if (rgba == NULL) { if (rgba == NULL) {
@ -325,35 +480,35 @@ static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
return rgba; return rgba;
} }
uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size,
uint8_t* output, int size, int stride) { uint8_t* output, size_t size, int stride) {
return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size); return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size);
} }
uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size,
uint8_t* output, int size, int stride) { uint8_t* output, size_t size, int stride) {
return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size); return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size);
} }
uint8_t* WebPDecodeARGBInto(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size,
uint8_t* output, int size, int stride) { uint8_t* output, size_t size, int stride) {
return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size); return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size);
} }
uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size,
uint8_t* output, int size, int stride) { uint8_t* output, size_t size, int stride) {
return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size); return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size);
} }
uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size,
uint8_t* output, int size, int stride) { uint8_t* output, size_t size, int stride) {
return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size); return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size);
} }
uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
uint8_t* luma, int luma_size, int luma_stride, uint8_t* luma, size_t luma_size, int luma_stride,
uint8_t* u, int u_size, int u_stride, uint8_t* u, size_t u_size, int u_stride,
uint8_t* v, int v_size, int v_stride) { uint8_t* v, size_t v_size, int v_stride) {
WebPDecParams params; WebPDecParams params;
WebPDecBuffer output; WebPDecBuffer output;
if (luma == NULL) return NULL; if (luma == NULL) return NULL;
@ -379,9 +534,9 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data, static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data,
uint32_t data_size, int* width, int* height, size_t data_size, int* const width, int* const height,
WebPDecBuffer* keep_info) { WebPDecBuffer* const keep_info) {
WebPDecParams params; WebPDecParams params;
WebPDecBuffer output; WebPDecBuffer output;
@ -394,53 +549,53 @@ static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data,
if (!WebPGetInfo(data, data_size, &output.width, &output.height)) { if (!WebPGetInfo(data, data_size, &output.width, &output.height)) {
return NULL; return NULL;
} }
if (width) *width = output.width; if (width != NULL) *width = output.width;
if (height) *height = output.height; if (height != NULL) *height = output.height;
// Decode // Decode
if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) { if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
return NULL; return NULL;
} }
if (keep_info) { // keep track of the side-info if (keep_info != NULL) { // keep track of the side-info
WebPCopyDecBuffer(&output, keep_info); WebPCopyDecBuffer(&output, keep_info);
} }
// return decoded samples (don't clear 'output'!) // return decoded samples (don't clear 'output'!)
return (mode >= MODE_YUV) ? output.u.YUVA.y : output.u.RGBA.rgba; return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y;
} }
uint8_t* WebPDecodeRGB(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
int* width, int* height) { int* width, int* height) {
return Decode(MODE_RGB, data, data_size, width, height, NULL); return Decode(MODE_RGB, data, data_size, width, height, NULL);
} }
uint8_t* WebPDecodeRGBA(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
int* width, int* height) { int* width, int* height) {
return Decode(MODE_RGBA, data, data_size, width, height, NULL); return Decode(MODE_RGBA, data, data_size, width, height, NULL);
} }
uint8_t* WebPDecodeARGB(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
int* width, int* height) { int* width, int* height) {
return Decode(MODE_ARGB, data, data_size, width, height, NULL); return Decode(MODE_ARGB, data, data_size, width, height, NULL);
} }
uint8_t* WebPDecodeBGR(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
int* width, int* height) { int* width, int* height) {
return Decode(MODE_BGR, data, data_size, width, height, NULL); return Decode(MODE_BGR, data, data_size, width, height, NULL);
} }
uint8_t* WebPDecodeBGRA(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
int* width, int* height) { int* width, int* height) {
return Decode(MODE_BGRA, data, data_size, width, height, NULL); return Decode(MODE_BGRA, data, data_size, width, height, NULL);
} }
uint8_t* WebPDecodeYUV(const uint8_t* data, uint32_t data_size, uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
int* width, int* height, uint8_t** u, uint8_t** v, int* width, int* height, uint8_t** u, uint8_t** v,
int* stride, int* uv_stride) { int* stride, int* uv_stride) {
WebPDecBuffer output; // only to preserve the side-infos WebPDecBuffer output; // only to preserve the side-infos
uint8_t* const out = Decode(MODE_YUV, data, data_size, uint8_t* const out = Decode(MODE_YUV, data, data_size,
width, height, &output); width, height, &output);
if (out) { if (out != NULL) {
const WebPYUVABuffer* const buf = &output.u.YUVA; const WebPYUVABuffer* const buf = &output.u.YUVA;
*u = buf->u; *u = buf->u;
*v = buf->v; *v = buf->v;
@ -452,69 +607,28 @@ uint8_t* WebPDecodeYUV(const uint8_t* data, uint32_t data_size,
} }
static void DefaultFeatures(WebPBitstreamFeatures* const features) { static void DefaultFeatures(WebPBitstreamFeatures* const features) {
assert(features); assert(features != NULL);
memset(features, 0, sizeof(*features)); memset(features, 0, sizeof(*features));
features->bitstream_version = 0; features->bitstream_version = 0;
} }
static VP8StatusCode GetFeatures(const uint8_t* data, uint32_t data_size, static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
WebPBitstreamFeatures* const features) { WebPBitstreamFeatures* const features) {
uint32_t vp8_chunk_size = 0; if (features == NULL || data == NULL) {
uint32_t riff_size = 0;
uint32_t flags = 0;
uint32_t vp8x_skip_size = 0;
uint32_t vp8_skip_size = 0;
VP8StatusCode status;
if (features == NULL) {
return VP8_STATUS_INVALID_PARAM; return VP8_STATUS_INVALID_PARAM;
} }
DefaultFeatures(features); DefaultFeatures(features);
if (data == NULL) { // Only parse enough of the data to retrieve width/height/has_alpha.
return VP8_STATUS_INVALID_PARAM; return ParseHeadersInternal(data, data_size,
} &features->width, &features->height,
&features->has_alpha, NULL);
// Skip over RIFF header.
status = WebPParseRIFF(&data, &data_size, &riff_size);
if (status != VP8_STATUS_OK) {
return status; // Wrong RIFF Header / Insufficient data.
}
// Skip over VP8X.
status = WebPParseVP8X(&data, &data_size, &vp8x_skip_size, &features->width,
&features->height, &flags);
if (status != VP8_STATUS_OK) {
return status; // Wrong VP8X / insufficient data.
}
if (vp8x_skip_size > 0) {
return VP8_STATUS_OK; // Return features from VP8X header.
}
// Skip over VP8 header.
status = WebPParseVP8Header(&data, &data_size, riff_size, &vp8_skip_size,
&vp8_chunk_size);
if (status != VP8_STATUS_OK) {
return status; // Wrong VP8 Chunk-header / insufficient data.
}
if (vp8_skip_size == 0) {
vp8_chunk_size = data_size; // No VP8 chunk wrapper over raw VP8 data.
}
// Validates raw VP8 data.
if (!VP8GetInfo(data, data_size, vp8_chunk_size,
&features->width, &features->height, &features->has_alpha)) {
return VP8_STATUS_BITSTREAM_ERROR;
}
return VP8_STATUS_OK; // Return features from VP8 header.
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// WebPGetInfo() // WebPGetInfo()
int WebPGetInfo(const uint8_t* data, uint32_t data_size, int WebPGetInfo(const uint8_t* data, size_t data_size,
int* width, int* height) { int* width, int* height) {
WebPBitstreamFeatures features; WebPBitstreamFeatures features;
@ -522,10 +636,10 @@ int WebPGetInfo(const uint8_t* data, uint32_t data_size,
return 0; return 0;
} }
if (width) { if (width != NULL) {
*width = features.width; *width = features.width;
} }
if (height) { if (height != NULL) {
*height = features.height; *height = features.height;
} }
@ -535,9 +649,9 @@ int WebPGetInfo(const uint8_t* data, uint32_t data_size,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Advance decoding API // Advance decoding API
int WebPInitDecoderConfigInternal(WebPDecoderConfig* const config, int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
int version) { int version) {
if (version != WEBP_DECODER_ABI_VERSION) { if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
return 0; // version mismatch return 0; // version mismatch
} }
if (config == NULL) { if (config == NULL) {
@ -549,11 +663,11 @@ int WebPInitDecoderConfigInternal(WebPDecoderConfig* const config,
return 1; return 1;
} }
VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, uint32_t data_size, VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size,
WebPBitstreamFeatures* const features, WebPBitstreamFeatures* features,
int version) { int version) {
VP8StatusCode status; VP8StatusCode status;
if (version != WEBP_DECODER_ABI_VERSION) { if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
return VP8_STATUS_INVALID_PARAM; // version mismatch return VP8_STATUS_INVALID_PARAM; // version mismatch
} }
if (features == NULL) { if (features == NULL) {
@ -562,24 +676,24 @@ VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, uint32_t data_size,
status = GetFeatures(data, data_size, features); status = GetFeatures(data, data_size, features);
if (status == VP8_STATUS_NOT_ENOUGH_DATA) { if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
return VP8_STATUS_BITSTREAM_ERROR; // Not enough data treated as error. return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error.
} }
return status; return status;
} }
VP8StatusCode WebPDecode(const uint8_t* data, uint32_t data_size, VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
WebPDecoderConfig* const config) { WebPDecoderConfig* config) {
WebPDecParams params; WebPDecParams params;
VP8StatusCode status; VP8StatusCode status;
if (!config) { if (config == NULL) {
return VP8_STATUS_INVALID_PARAM; return VP8_STATUS_INVALID_PARAM;
} }
status = GetFeatures(data, data_size, &config->input); status = GetFeatures(data, data_size, &config->input);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
if (status == VP8_STATUS_NOT_ENOUGH_DATA) { if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
return VP8_STATUS_BITSTREAM_ERROR; // Not enough data treated as error. return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error.
} }
return status; return status;
} }
@ -592,6 +706,66 @@ VP8StatusCode WebPDecode(const uint8_t* data, uint32_t data_size,
return status; return status;
} }
//------------------------------------------------------------------------------
// Cropping and rescaling.
int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
VP8Io* const io, WEBP_CSP_MODE src_colorspace) {
const int W = io->width;
const int H = io->height;
int x = 0, y = 0, w = W, h = H;
// Cropping
io->use_cropping = (options != NULL) && (options->use_cropping > 0);
if (io->use_cropping) {
w = options->crop_width;
h = options->crop_height;
x = options->crop_left;
y = options->crop_top;
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 or YUV422
x &= ~1;
y &= ~1; // TODO(later): only for YUV420, not YUV422.
}
if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
return 0; // out of frame boundary error
}
}
io->crop_left = x;
io->crop_top = y;
io->crop_right = x + w;
io->crop_bottom = y + h;
io->mb_w = w;
io->mb_h = h;
// Scaling
io->use_scaling = (options != NULL) && (options->use_scaling > 0);
if (io->use_scaling) {
if (options->scaled_width <= 0 || options->scaled_height <= 0) {
return 0;
}
io->scaled_width = options->scaled_width;
io->scaled_height = options->scaled_height;
}
// Filter
io->bypass_filtering = options && options->bypass_filtering;
// Fancy upsampler
#ifdef FANCY_UPSAMPLING
io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling);
#endif
if (io->use_scaling) {
// disable filter (only for large downscaling ratio).
io->bypass_filtering = (io->scaled_width < W * 3 / 4) &&
(io->scaled_height < H * 3 / 4);
io->fancy_upsampling = 0;
}
return 1;
}
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -16,29 +16,15 @@
extern "C" { extern "C" {
#endif #endif
#include "../webp/decode_vp8.h" #include "../utils/rescaler.h"
#include "./decode_vp8.h"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// WebPDecParams: Decoding output parameters. Transient internal object. // WebPDecParams: Decoding output parameters. Transient internal object.
typedef struct WebPDecParams WebPDecParams; typedef struct WebPDecParams WebPDecParams;
typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p); typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p);
typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos);
// Structure use for on-the-fly rescaling
typedef struct {
int x_expand; // true if we're expanding in the x direction
int fy_scale, fx_scale; // fixed-point scaling factor
int64_t fxy_scale; // ''
// we need hpel-precise add/sub increments, for the downsampled U/V planes.
int y_accum; // vertical accumulator
int y_add, y_sub; // vertical increments (add ~= src, sub ~= dst)
int x_add, x_sub; // horizontal increments (add ~= src, sub ~= dst)
int src_width, src_height; // source dimensions
int dst_width, dst_height; // destination dimensions
uint8_t* dst;
int dst_stride;
int32_t* irow, *frow; // work buffer
} WebPRescaler;
struct WebPDecParams { struct WebPDecParams {
WebPDecBuffer* output; // output buffer. WebPDecBuffer* output; // output buffer.
@ -49,9 +35,11 @@ struct WebPDecParams {
const WebPDecoderOptions* options; // if not NULL, use alt decoding features const WebPDecoderOptions* options; // if not NULL, use alt decoding features
// rescalers // rescalers
WebPRescaler scaler_y, scaler_u, scaler_v, scaler_a; WebPRescaler scaler_y, scaler_u, scaler_v, scaler_a;
void* memory; // overall scratch memory for the output work. void* memory; // overall scratch memory for the output work.
OutputFunc emit; // output RGB or YUV samples
OutputFunc emit_alpha; // output alpha channel OutputFunc emit; // output RGB or YUV samples
OutputFunc emit_alpha; // output alpha channel
OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values
}; };
// Should be called first, before any use of the WebPDecParams object. // Should be called first, before any use of the WebPDecParams object.
@ -60,69 +48,25 @@ void WebPResetDecParams(WebPDecParams* const params);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Header parsing helpers // Header parsing helpers
#define TAG_SIZE 4 // Structure storing a description of the RIFF headers.
#define CHUNK_HEADER_SIZE 8 typedef struct {
#define RIFF_HEADER_SIZE 12 const uint8_t* data; // input buffer
#define FRAME_CHUNK_SIZE 20 size_t data_size; // input buffer size
#define LOOP_CHUNK_SIZE 4 size_t offset; // offset to main data chunk (VP8 or VP8L)
#define TILE_CHUNK_SIZE 8 const uint8_t* alpha_data; // points to alpha chunk (if present)
#define VP8X_CHUNK_SIZE 12 size_t alpha_data_size; // alpha chunk size
#define VP8_FRAME_HEADER_SIZE 10 // Size of the frame header within VP8 data. size_t compressed_size; // VP8/VP8L compressed data size
size_t riff_size; // size of the riff payload (or 0 if absent)
int is_lossless; // true if a VP8L chunk is present
} WebPHeaderStructure;
// Validates the RIFF container (if detected) and skips over it. // Skips over all valid chunks prior to the first VP8/VP8L frame header.
// If a RIFF container is detected,
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and
// VP8_STATUS_OK otherwise.
// In case there are not enough bytes (partial RIFF container), return 0 for
// riff_size. Else return the riff_size extracted from the header.
VP8StatusCode WebPParseRIFF(const uint8_t** data, uint32_t* data_size,
uint32_t* riff_size);
// Validates the VP8X Header and skips over it.
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header,
// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
// VP8_STATUS_OK otherwise.
// If a VP8 chunk is found, bytes_skipped is set to the total number of bytes
// that are skipped; also Width, Height & Flags are set to the corresponding
// fields extracted from the VP8X chunk.
VP8StatusCode WebPParseVP8X(const uint8_t** data, uint32_t* data_size,
uint32_t* bytes_skipped,
int* width, int* height, uint32_t* flags);
// Skips to the next VP8 chunk header in the data given the size of the RIFF
// chunk 'riff_size'.
// Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered,
// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
// VP8_STATUS_OK otherwise.
// If a VP8 chunk is found, bytes_skipped is set to the total number of bytes
// that are skipped.
VP8StatusCode WebPParseOptionalChunks(const uint8_t** data, uint32_t* data_size,
uint32_t riff_size,
uint32_t* bytes_skipped);
// Validates the VP8 Header ("VP8 nnnn") and skips over it.
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid (vp8_chunk_size greater than
// riff_size) VP8 header,
// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
// VP8_STATUS_OK otherwise.
// If a VP8 chunk is found, bytes_skipped is set to the total number of bytes
// that are skipped and vp8_chunk_size is set to the corresponding size
// extracted from the VP8 chunk header.
// For a partial VP8 chunk, vp8_chunk_size is set to 0.
VP8StatusCode WebPParseVP8Header(const uint8_t** data, uint32_t* data_size,
uint32_t riff_size, uint32_t* bytes_skipped,
uint32_t* vp8_chunk_size);
// Skips over all valid chunks prior to the first VP8 frame header.
// Returns VP8_STATUS_OK on success, // Returns VP8_STATUS_OK on success,
// VP8_STATUS_BITSTREAM_ERROR if an invalid header/chunk is found, and // VP8_STATUS_BITSTREAM_ERROR if an invalid header/chunk is found, and
// VP8_STATUS_NOT_ENOUGH_DATA if case of insufficient data. // VP8_STATUS_NOT_ENOUGH_DATA if case of insufficient data.
// Also, data, data_size, vp8_size & bytes_skipped are updated appropriately // In 'headers', compressed_size, offset, alpha_data, alpha_size and lossless
// on success, where // fields are updated appropriately upon success.
// vp8_size is the size of VP8 chunk data (extracted from VP8 chunk header) and VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers);
// bytes_skipped is set to the total number of bytes that are skipped.
VP8StatusCode WebPParseHeaders(const uint8_t** data, uint32_t* data_size,
uint32_t* vp8_size, uint32_t* bytes_skipped);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Misc utils // Misc utils
@ -131,6 +75,11 @@ VP8StatusCode WebPParseHeaders(const uint8_t** data, uint32_t* data_size,
// hooks will use the supplied 'params' as io->opaque handle. // hooks will use the supplied 'params' as io->opaque handle.
void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io); void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io);
// Setup crop_xxx fields, mb_w and mb_h in io. 'src_colorspace' refers
// to the *compressed* format, not the output one.
int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
VP8Io* const io, WEBP_CSP_MODE src_colorspace);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Internal functions regarding WebPDecBuffer memory (in buffer.c). // Internal functions regarding WebPDecBuffer memory (in buffer.c).
// Don't really need to be externally visible for now. // Don't really need to be externally visible for now.
@ -154,6 +103,8 @@ void WebPCopyDecBuffer(const WebPDecBuffer* const src,
// Copy and transfer ownership from src to dst (beware of parameter order!) // Copy and transfer ownership from src to dst (beware of parameter order!)
void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst); void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)

View File

@ -1,14 +1,26 @@
AM_CPPFLAGS = -I$(top_srcdir)/src AM_CPPFLAGS = -I$(top_srcdir)/src
libwebpdsp_la_SOURCES = dsp.h cpu.c \
enc.c enc_sse2.c \
dec.c dec_sse2.c dec_neon.c \
upsampling.c upsampling_sse2.c \
yuv.h yuv.c
libwebpdsp_la_LDFLAGS = -version-info 0:0:0 -lm
libwebpdsp_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
libwebpdspinclude_HEADERS = ../webp/types.h
libwebpdspincludedir = $(includedir)/webp
noinst_HEADERS = dsp.h yuv.h
noinst_LTLIBRARIES = libwebpdsp.la noinst_LTLIBRARIES = libwebpdsp.la
libwebpdsp_la_SOURCES =
libwebpdsp_la_SOURCES += cpu.c
libwebpdsp_la_SOURCES += dec.c
libwebpdsp_la_SOURCES += dec_neon.c
libwebpdsp_la_SOURCES += dec_sse2.c
libwebpdsp_la_SOURCES += dsp.h
libwebpdsp_la_SOURCES += enc.c
libwebpdsp_la_SOURCES += enc_sse2.c
libwebpdsp_la_SOURCES += lossless.c
libwebpdsp_la_SOURCES += lossless.h
libwebpdsp_la_SOURCES += upsampling.c
libwebpdsp_la_SOURCES += upsampling_sse2.c
libwebpdsp_la_SOURCES += yuv.c
libwebpdsp_la_SOURCES += yuv.h
libwebpdspinclude_HEADERS = ../webp/types.h
noinst_HEADERS =
noinst_HEADERS += ../dec/decode_vp8.h
noinst_HEADERS += ../webp/decode.h
libwebpdsp_la_LDFLAGS = -lm
libwebpdsp_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
libwebpdspincludedir = $(includedir)/webp

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,10 +9,12 @@
// //
// Author: Christian Duvivier (cduvivier@google.com) // Author: Christian Duvivier (cduvivier@google.com)
#include <stddef.h> // for NULL
#include "./dsp.h" #include "./dsp.h"
#if defined(__ANDROID__)
#include <cpu-features.h>
#endif
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
@ -21,8 +23,9 @@ extern "C" {
// SSE2 detection. // SSE2 detection.
// //
#if defined(__pic__) && defined(__i386__) // apple/darwin gcc-4.0.1 defines __PIC__, but not __pic__ with -fPIC.
static inline void GetCPUInfo(int cpu_info[4], int info_type) { #if (defined(__pic__) || defined(__PIC__)) && defined(__i386__)
static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
__asm__ volatile ( __asm__ volatile (
"mov %%ebx, %%edi\n" "mov %%ebx, %%edi\n"
"cpuid\n" "cpuid\n"
@ -31,17 +34,17 @@ static inline void GetCPUInfo(int cpu_info[4], int info_type) {
: "a"(info_type)); : "a"(info_type));
} }
#elif defined(__i386__) || defined(__x86_64__) #elif defined(__i386__) || defined(__x86_64__)
static inline void GetCPUInfo(int cpu_info[4], int info_type) { static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
__asm__ volatile ( __asm__ volatile (
"cpuid\n" "cpuid\n"
: "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
: "a"(info_type)); : "a"(info_type));
} }
#elif defined(_MSC_VER) // Visual C++ #elif defined(WEBP_MSC_SSE2)
#define GetCPUInfo __cpuid #define GetCPUInfo __cpuid
#endif #endif
#if defined(__i386__) || defined(__x86_64__) || defined(_MSC_VER) #if defined(__i386__) || defined(__x86_64__) || defined(WEBP_MSC_SSE2)
static int x86CPUInfo(CPUFeature feature) { static int x86CPUInfo(CPUFeature feature) {
int cpu_info[4]; int cpu_info[4];
GetCPUInfo(cpu_info, 1); GetCPUInfo(cpu_info, 1);
@ -54,10 +57,22 @@ static int x86CPUInfo(CPUFeature feature) {
return 0; return 0;
} }
VP8CPUInfo VP8GetCPUInfo = x86CPUInfo; VP8CPUInfo VP8GetCPUInfo = x86CPUInfo;
#elif defined(WEBP_ANDROID_NEON)
static int AndroidCPUInfo(CPUFeature feature) {
const AndroidCpuFamily cpu_family = android_getCpuFamily();
const uint64_t cpu_features = android_getCpuFeatures();
if (feature == kNEON) {
return (cpu_family == ANDROID_CPU_FAMILY_ARM &&
0 != (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON));
}
return 0;
}
VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo;
#elif defined(__ARM_NEON__) #elif defined(__ARM_NEON__)
// define a dummy function to enable turning off NEON at runtime by setting // define a dummy function to enable turning off NEON at runtime by setting
// VP8DecGetCPUInfo = NULL // VP8DecGetCPUInfo = NULL
static int armCPUInfo(CPUFeature feature) { static int armCPUInfo(CPUFeature feature) {
(void)feature;
return 1; return 1;
} }
VP8CPUInfo VP8GetCPUInfo = armCPUInfo; VP8CPUInfo VP8GetCPUInfo = armCPUInfo;

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -49,7 +49,7 @@ static void DspInitTables(void) {
} }
} }
static inline uint8_t clip_8b(int v) { static WEBP_INLINE uint8_t clip_8b(int v) {
return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255; return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
} }
@ -171,7 +171,7 @@ void (*VP8TransformWHT)(const int16_t* in, int16_t* out) = TransformWHT;
#define DST(x, y) dst[(x) + (y) * BPS] #define DST(x, y) dst[(x) + (y) * BPS]
static inline void TrueMotion(uint8_t *dst, int size) { static WEBP_INLINE void TrueMotion(uint8_t *dst, int size) {
const uint8_t* top = dst - BPS; const uint8_t* top = dst - BPS;
const uint8_t* const clip0 = clip1 + 255 - top[-1]; const uint8_t* const clip0 = clip1 + 255 - top[-1];
int y; int y;
@ -206,7 +206,7 @@ static void HE16(uint8_t *dst) { // horizontal
} }
} }
static inline void Put16(int v, uint8_t* dst) { static WEBP_INLINE void Put16(int v, uint8_t* dst) {
int j; int j;
for (j = 0; j < 16; ++j) { for (j = 0; j < 16; ++j) {
memset(dst + j * BPS, v, 16); memset(dst + j * BPS, v, 16);
@ -426,7 +426,7 @@ static void HE8uv(uint8_t *dst) { // horizontal
} }
// helper for chroma-DC predictions // helper for chroma-DC predictions
static inline void Put8x8uv(uint64_t v, uint8_t* dst) { static WEBP_INLINE void Put8x8uv(uint64_t v, uint8_t* dst) {
int j; int j;
for (j = 0; j < 8; ++j) { for (j = 0; j < 8; ++j) {
*(uint64_t*)(dst + j * BPS) = v; *(uint64_t*)(dst + j * BPS) = v;
@ -467,16 +467,16 @@ static void DC8uvNoTopLeft(uint8_t *dst) { // DC with nothing
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// default C implementations // default C implementations
VP8PredFunc VP8PredLuma4[/* NUM_BMODES */] = { const VP8PredFunc VP8PredLuma4[NUM_BMODES] = {
DC4, TM4, VE4, HE4, RD4, VR4, LD4, VL4, HD4, HU4 DC4, TM4, VE4, HE4, RD4, VR4, LD4, VL4, HD4, HU4
}; };
VP8PredFunc VP8PredLuma16[/*NUM_B_DC_MODES */] = { const VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES] = {
DC16, TM16, VE16, HE16, DC16, TM16, VE16, HE16,
DC16NoTop, DC16NoLeft, DC16NoTopLeft DC16NoTop, DC16NoLeft, DC16NoTopLeft
}; };
VP8PredFunc VP8PredChroma8[/*NUM_B_DC_MODES */] = { const VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES] = {
DC8uv, TM8uv, VE8uv, HE8uv, DC8uv, TM8uv, VE8uv, HE8uv,
DC8uvNoTop, DC8uvNoLeft, DC8uvNoTopLeft DC8uvNoTop, DC8uvNoLeft, DC8uvNoTopLeft
}; };
@ -485,7 +485,7 @@ VP8PredFunc VP8PredChroma8[/*NUM_B_DC_MODES */] = {
// Edge filtering functions // Edge filtering functions
// 4 pixels in, 2 pixels out // 4 pixels in, 2 pixels out
static inline void do_filter2(uint8_t* p, int step) { static WEBP_INLINE void do_filter2(uint8_t* p, int step) {
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1]; const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1];
const int a1 = sclip2[112 + ((a + 4) >> 3)]; const int a1 = sclip2[112 + ((a + 4) >> 3)];
@ -495,7 +495,7 @@ static inline void do_filter2(uint8_t* p, int step) {
} }
// 4 pixels in, 4 pixels out // 4 pixels in, 4 pixels out
static inline void do_filter4(uint8_t* p, int step) { static WEBP_INLINE void do_filter4(uint8_t* p, int step) {
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
const int a = 3 * (q0 - p0); const int a = 3 * (q0 - p0);
const int a1 = sclip2[112 + ((a + 4) >> 3)]; const int a1 = sclip2[112 + ((a + 4) >> 3)];
@ -508,7 +508,7 @@ static inline void do_filter4(uint8_t* p, int step) {
} }
// 6 pixels in, 6 pixels out // 6 pixels in, 6 pixels out
static inline void do_filter6(uint8_t* p, int step) { static WEBP_INLINE void do_filter6(uint8_t* p, int step) {
const int p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step]; const int p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
const int q0 = p[0], q1 = p[step], q2 = p[2*step]; const int q0 = p[0], q1 = p[step], q2 = p[2*step];
const int a = sclip1[1020 + 3 * (q0 - p0) + sclip1[1020 + p1 - q1]]; const int a = sclip1[1020 + 3 * (q0 - p0) + sclip1[1020 + p1 - q1]];
@ -523,17 +523,18 @@ static inline void do_filter6(uint8_t* p, int step) {
p[ 2*step] = clip1[255 + q2 - a3]; p[ 2*step] = clip1[255 + q2 - a3];
} }
static inline int hev(const uint8_t* p, int step, int thresh) { static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) {
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh); return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh);
} }
static inline int needs_filter(const uint8_t* p, int step, int thresh) { static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int thresh) {
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh; return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh;
} }
static inline int needs_filter2(const uint8_t* p, int step, int t, int it) { static WEBP_INLINE int needs_filter2(const uint8_t* p,
int step, int t, int it) {
const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step]; const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step]; const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step];
if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t) if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t)
@ -583,8 +584,9 @@ static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Complex In-loop filtering (Paragraph 15.3) // Complex In-loop filtering (Paragraph 15.3)
static inline void FilterLoop26(uint8_t* p, int hstride, int vstride, int size, static WEBP_INLINE void FilterLoop26(uint8_t* p,
int thresh, int ithresh, int hev_thresh) { int hstride, int vstride, int size,
int thresh, int ithresh, int hev_thresh) {
while (size-- > 0) { while (size-- > 0) {
if (needs_filter2(p, hstride, thresh, ithresh)) { if (needs_filter2(p, hstride, thresh, ithresh)) {
if (hev(p, hstride, hev_thresh)) { if (hev(p, hstride, hev_thresh)) {
@ -597,8 +599,9 @@ static inline void FilterLoop26(uint8_t* p, int hstride, int vstride, int size,
} }
} }
static inline void FilterLoop24(uint8_t* p, int hstride, int vstride, int size, static WEBP_INLINE void FilterLoop24(uint8_t* p,
int thresh, int ithresh, int hev_thresh) { int hstride, int vstride, int size,
int thresh, int ithresh, int hev_thresh) {
while (size-- > 0) { while (size-- > 0) {
if (needs_filter2(p, hstride, thresh, ithresh)) { if (needs_filter2(p, hstride, thresh, ithresh)) {
if (hev(p, hstride, hev_thresh)) { if (hev(p, hstride, hev_thresh)) {
@ -712,11 +715,11 @@ void VP8DspInit(void) {
// If defined, use CPUInfo() to overwrite some pointers with faster versions. // If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8GetCPUInfo) { if (VP8GetCPUInfo) {
#if defined(__SSE2__) || defined(_MSC_VER) #if defined(WEBP_USE_SSE2)
if (VP8GetCPUInfo(kSSE2)) { if (VP8GetCPUInfo(kSSE2)) {
VP8DspInitSSE2(); VP8DspInitSSE2();
} }
#elif defined(__GNUC__) && defined(__ARM_NEON__) #elif defined(WEBP_USE_NEON)
if (VP8GetCPUInfo(kNEON)) { if (VP8GetCPUInfo(kNEON)) {
VP8DspInitNEON(); VP8DspInitNEON();
} }

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2012 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -7,9 +7,12 @@
// //
// ARM NEON version of dsp functions and loop filtering. // ARM NEON version of dsp functions and loop filtering.
// //
// Author: somnath@google.com (Somnath Banerjee) // Authors: Somnath Banerjee (somnath@google.com)
// Johann Koenig (johannkoenig@google.com)
#if defined(__GNUC__) && defined(__ARM_NEON__) #include "./dsp.h"
#if defined(WEBP_USE_NEON)
#include "../dec/vp8i.h" #include "../dec/vp8i.h"
@ -152,9 +155,167 @@ static void SimpleHFilter16iNEON(uint8_t* p, int stride, int thresh) {
} }
} }
static void TransformOneNEON(const int16_t *in, uint8_t *dst) {
const int kBPS = BPS;
const int16_t constants[] = {20091, 17734, 0, 0};
/* kC1, kC2. Padded because vld1.16 loads 8 bytes
* Technically these are unsigned but vqdmulh is only available in signed.
* vqdmulh returns high half (effectively >> 16) but also doubles the value,
* changing the >> 16 to >> 15 and requiring an additional >> 1.
* We use this to our advantage with kC2. The canonical value is 35468.
* However, the high bit is set so treating it as signed will give incorrect
* results. We avoid this by down shifting by 1 here to clear the highest bit.
* Combined with the doubling effect of vqdmulh we get >> 16.
* This can not be applied to kC1 because the lowest bit is set. Down shifting
* the constant would reduce precision.
*/
/* libwebp uses a trick to avoid some extra addition that libvpx does.
* Instead of:
* temp2 = ip[12] + ((ip[12] * cospi8sqrt2minus1) >> 16);
* libwebp adds 1 << 16 to cospi8sqrt2minus1 (kC1). However, this causes the
* same issue with kC1 and vqdmulh that we work around by down shifting kC2
*/
/* Adapted from libvpx: vp8/common/arm/neon/shortidct4x4llm_neon.asm */
__asm__ volatile (
"vld1.16 {q1, q2}, [%[in]] \n"
"vld1.16 {d0}, [%[constants]] \n"
/* d2: in[0]
* d3: in[8]
* d4: in[4]
* d5: in[12]
*/
"vswp d3, d4 \n"
/* q8 = {in[4], in[12]} * kC1 * 2 >> 16
* q9 = {in[4], in[12]} * kC2 >> 16
*/
"vqdmulh.s16 q8, q2, d0[0] \n"
"vqdmulh.s16 q9, q2, d0[1] \n"
/* d22 = a = in[0] + in[8]
* d23 = b = in[0] - in[8]
*/
"vqadd.s16 d22, d2, d3 \n"
"vqsub.s16 d23, d2, d3 \n"
/* The multiplication should be x * kC1 >> 16
* However, with vqdmulh we get x * kC1 * 2 >> 16
* (multiply, double, return high half)
* We avoided this in kC2 by pre-shifting the constant.
* q8 = in[4]/[12] * kC1 >> 16
*/
"vshr.s16 q8, q8, #1 \n"
/* Add {in[4], in[12]} back after the multiplication. This is handled by
* adding 1 << 16 to kC1 in the libwebp C code.
*/
"vqadd.s16 q8, q2, q8 \n"
/* d20 = c = in[4]*kC2 - in[12]*kC1
* d21 = d = in[4]*kC1 + in[12]*kC2
*/
"vqsub.s16 d20, d18, d17 \n"
"vqadd.s16 d21, d19, d16 \n"
/* d2 = tmp[0] = a + d
* d3 = tmp[1] = b + c
* d4 = tmp[2] = b - c
* d5 = tmp[3] = a - d
*/
"vqadd.s16 d2, d22, d21 \n"
"vqadd.s16 d3, d23, d20 \n"
"vqsub.s16 d4, d23, d20 \n"
"vqsub.s16 d5, d22, d21 \n"
"vzip.16 q1, q2 \n"
"vzip.16 q1, q2 \n"
"vswp d3, d4 \n"
/* q8 = {tmp[4], tmp[12]} * kC1 * 2 >> 16
* q9 = {tmp[4], tmp[12]} * kC2 >> 16
*/
"vqdmulh.s16 q8, q2, d0[0] \n"
"vqdmulh.s16 q9, q2, d0[1] \n"
/* d22 = a = tmp[0] + tmp[8]
* d23 = b = tmp[0] - tmp[8]
*/
"vqadd.s16 d22, d2, d3 \n"
"vqsub.s16 d23, d2, d3 \n"
/* See long winded explanations prior */
"vshr.s16 q8, q8, #1 \n"
"vqadd.s16 q8, q2, q8 \n"
/* d20 = c = in[4]*kC2 - in[12]*kC1
* d21 = d = in[4]*kC1 + in[12]*kC2
*/
"vqsub.s16 d20, d18, d17 \n"
"vqadd.s16 d21, d19, d16 \n"
/* d2 = tmp[0] = a + d
* d3 = tmp[1] = b + c
* d4 = tmp[2] = b - c
* d5 = tmp[3] = a - d
*/
"vqadd.s16 d2, d22, d21 \n"
"vqadd.s16 d3, d23, d20 \n"
"vqsub.s16 d4, d23, d20 \n"
"vqsub.s16 d5, d22, d21 \n"
"vld1.32 d6[0], [%[dst]], %[kBPS] \n"
"vld1.32 d6[1], [%[dst]], %[kBPS] \n"
"vld1.32 d7[0], [%[dst]], %[kBPS] \n"
"vld1.32 d7[1], [%[dst]], %[kBPS] \n"
"sub %[dst], %[dst], %[kBPS], lsl #2 \n"
/* (val) + 4 >> 3 */
"vrshr.s16 d2, d2, #3 \n"
"vrshr.s16 d3, d3, #3 \n"
"vrshr.s16 d4, d4, #3 \n"
"vrshr.s16 d5, d5, #3 \n"
"vzip.16 q1, q2 \n"
"vzip.16 q1, q2 \n"
/* Must accumulate before saturating */
"vmovl.u8 q8, d6 \n"
"vmovl.u8 q9, d7 \n"
"vqadd.s16 q1, q1, q8 \n"
"vqadd.s16 q2, q2, q9 \n"
"vqmovun.s16 d0, q1 \n"
"vqmovun.s16 d1, q2 \n"
"vst1.32 d0[0], [%[dst]], %[kBPS] \n"
"vst1.32 d0[1], [%[dst]], %[kBPS] \n"
"vst1.32 d1[0], [%[dst]], %[kBPS] \n"
"vst1.32 d1[1], [%[dst]] \n"
: [in] "+r"(in), [dst] "+r"(dst) /* modified registers */
: [kBPS] "r"(kBPS), [constants] "r"(constants) /* constants */
: "memory", "q0", "q1", "q2", "q8", "q9", "q10", "q11" /* clobbered */
);
}
static void TransformTwoNEON(const int16_t* in, uint8_t* dst, int do_two) {
TransformOneNEON(in, dst);
if (do_two) {
TransformOneNEON(in + 16, dst + 4);
}
}
extern void VP8DspInitNEON(void); extern void VP8DspInitNEON(void);
void VP8DspInitNEON(void) { void VP8DspInitNEON(void) {
VP8Transform = TransformTwoNEON;
VP8SimpleVFilter16 = SimpleVFilter16NEON; VP8SimpleVFilter16 = SimpleVFilter16NEON;
VP8SimpleHFilter16 = SimpleHFilter16NEON; VP8SimpleHFilter16 = SimpleHFilter16NEON;
VP8SimpleVFilter16i = SimpleVFilter16iNEON; VP8SimpleVFilter16i = SimpleVFilter16iNEON;
@ -165,4 +326,4 @@ void VP8DspInitNEON(void) {
} // extern "C" } // extern "C"
#endif #endif
#endif // __GNUC__ && __ARM_NEON__ #endif // WEBP_USE_NEON

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -10,7 +10,9 @@
// Author: somnath@google.com (Somnath Banerjee) // Author: somnath@google.com (Somnath Banerjee)
// cduvivier@google.com (Christian Duvivier) // cduvivier@google.com (Christian Duvivier)
#if defined(__SSE2__) || defined(_MSC_VER) #include "./dsp.h"
#if defined(WEBP_USE_SSE2)
#include <emmintrin.h> #include <emmintrin.h>
#include "../dec/vp8i.h" #include "../dec/vp8i.h"
@ -341,8 +343,8 @@ static void NeedsFilter(const __m128i* p1, const __m128i* p0, const __m128i* q0,
// Edge filtering functions // Edge filtering functions
// Applies filter on 2 pixels (p0 and q0) // Applies filter on 2 pixels (p0 and q0)
static inline void DoFilter2(const __m128i* p1, __m128i* p0, __m128i* q0, static WEBP_INLINE void DoFilter2(const __m128i* p1, __m128i* p0, __m128i* q0,
const __m128i* q1, int thresh) { const __m128i* q1, int thresh) {
__m128i a, mask; __m128i a, mask;
const __m128i sign_bit = _mm_set1_epi8(0x80); const __m128i sign_bit = _mm_set1_epi8(0x80);
const __m128i p1s = _mm_xor_si128(*p1, sign_bit); const __m128i p1s = _mm_xor_si128(*p1, sign_bit);
@ -362,8 +364,9 @@ static inline void DoFilter2(const __m128i* p1, __m128i* p0, __m128i* q0,
} }
// Applies filter on 4 pixels (p1, p0, q0 and q1) // Applies filter on 4 pixels (p1, p0, q0 and q1)
static inline void DoFilter4(__m128i* p1, __m128i *p0, __m128i* q0, __m128i* q1, static WEBP_INLINE void DoFilter4(__m128i* p1, __m128i *p0,
const __m128i* mask, int hev_thresh) { __m128i* q0, __m128i* q1,
const __m128i* mask, int hev_thresh) {
__m128i not_hev; __m128i not_hev;
__m128i t1, t2, t3; __m128i t1, t2, t3;
const __m128i sign_bit = _mm_set1_epi8(0x80); const __m128i sign_bit = _mm_set1_epi8(0x80);
@ -408,9 +411,9 @@ static inline void DoFilter4(__m128i* p1, __m128i *p0, __m128i* q0, __m128i* q1,
} }
// Applies filter on 6 pixels (p2, p1, p0, q0, q1 and q2) // Applies filter on 6 pixels (p2, p1, p0, q0, q1 and q2)
static inline void DoFilter6(__m128i *p2, __m128i* p1, __m128i *p0, static WEBP_INLINE void DoFilter6(__m128i *p2, __m128i* p1, __m128i *p0,
__m128i* q0, __m128i* q1, __m128i *q2, __m128i* q0, __m128i* q1, __m128i *q2,
const __m128i* mask, int hev_thresh) { const __m128i* mask, int hev_thresh) {
__m128i a, not_hev; __m128i a, not_hev;
const __m128i sign_bit = _mm_set1_epi8(0x80); const __m128i sign_bit = _mm_set1_epi8(0x80);
@ -466,8 +469,8 @@ static inline void DoFilter6(__m128i *p2, __m128i* p1, __m128i *p0,
// //
// TODO(somnath): Investigate _mm_shuffle* also see if it can be broken into // TODO(somnath): Investigate _mm_shuffle* also see if it can be broken into
// two Load4x4() to avoid code duplication. // two Load4x4() to avoid code duplication.
static inline void Load8x4(const uint8_t* b, int stride, static WEBP_INLINE void Load8x4(const uint8_t* b, int stride,
__m128i* p, __m128i* q) { __m128i* p, __m128i* q) {
__m128i t1, t2; __m128i t1, t2;
// Load 0th, 1st, 4th and 5th rows // Load 0th, 1st, 4th and 5th rows
@ -506,9 +509,10 @@ static inline void Load8x4(const uint8_t* b, int stride,
*q = _mm_unpackhi_epi32(t1, t2); *q = _mm_unpackhi_epi32(t1, t2);
} }
static inline void Load16x4(const uint8_t* r0, const uint8_t* r8, int stride, static WEBP_INLINE void Load16x4(const uint8_t* r0, const uint8_t* r8,
__m128i* p1, __m128i* p0, int stride,
__m128i* q0, __m128i* q1) { __m128i* p1, __m128i* p0,
__m128i* q0, __m128i* q1) {
__m128i t1, t2; __m128i t1, t2;
// Assume the pixels around the edge (|) are numbered as follows // Assume the pixels around the edge (|) are numbered as follows
// 00 01 | 02 03 // 00 01 | 02 03
@ -540,7 +544,7 @@ static inline void Load16x4(const uint8_t* r0, const uint8_t* r8, int stride,
*q1 = _mm_unpackhi_epi64(t2, *q1); *q1 = _mm_unpackhi_epi64(t2, *q1);
} }
static inline void Store4x4(__m128i* x, uint8_t* dst, int stride) { static WEBP_INLINE void Store4x4(__m128i* x, uint8_t* dst, int stride) {
int i; int i;
for (i = 0; i < 4; ++i, dst += stride) { for (i = 0; i < 4; ++i, dst += stride) {
*((int32_t*)dst) = _mm_cvtsi128_si32(*x); *((int32_t*)dst) = _mm_cvtsi128_si32(*x);
@ -549,8 +553,9 @@ static inline void Store4x4(__m128i* x, uint8_t* dst, int stride) {
} }
// Transpose back and store // Transpose back and store
static inline void Store16x4(uint8_t* r0, uint8_t* r8, int stride, __m128i* p1, static WEBP_INLINE void Store16x4(uint8_t* r0, uint8_t* r8, int stride,
__m128i* p0, __m128i* q0, __m128i* q1) { __m128i* p1, __m128i* p0,
__m128i* q0, __m128i* q1) {
__m128i t1; __m128i t1;
// p0 = 71 70 61 60 51 50 41 40 31 30 21 20 11 10 01 00 // p0 = 71 70 61 60 51 50 41 40 31 30 21 20 11 10 01 00
@ -895,4 +900,4 @@ void VP8DspInitSSE2(void) {
} // extern "C" } // extern "C"
#endif #endif
#endif //__SSE2__ || _MSC_VER #endif // WEBP_USE_SSE2

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -21,6 +21,22 @@ extern "C" {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// CPU detection // CPU detection
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
#define WEBP_MSC_SSE2 // Visual C++ SSE2 targets
#endif
#if defined(__SSE2__) || defined(WEBP_MSC_SSE2)
#define WEBP_USE_SSE2
#endif
#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
#define WEBP_ANDROID_NEON // Android targets that might support NEON
#endif
#if defined(__ARM_NEON__) || defined(WEBP_ANDROID_NEON)
#define WEBP_USE_NEON
#endif
typedef enum { typedef enum {
kSSE2, kSSE2,
kSSE3, kSSE3,
@ -63,8 +79,6 @@ extern VP8WMetric VP8TDisto4x4, VP8TDisto16x16;
typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst); typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst);
extern VP8BlockCopy VP8Copy4x4; extern VP8BlockCopy VP8Copy4x4;
extern VP8BlockCopy VP8Copy8x8;
extern VP8BlockCopy VP8Copy16x16;
// Quantization // Quantization
struct VP8Matrix; // forward declaration struct VP8Matrix; // forward declaration
typedef int (*VP8QuantizeBlock)(int16_t in[16], int16_t out[16], typedef int (*VP8QuantizeBlock)(int16_t in[16], int16_t out[16],
@ -95,9 +109,9 @@ extern void (*VP8TransformWHT)(const int16_t* in, int16_t* out);
// *dst is the destination block, with stride BPS. Boundary samples are // *dst is the destination block, with stride BPS. Boundary samples are
// assumed accessible when needed. // assumed accessible when needed.
typedef void (*VP8PredFunc)(uint8_t* dst); typedef void (*VP8PredFunc)(uint8_t* dst);
extern VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */]; extern const VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */];
extern VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */]; extern const VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */];
extern VP8PredFunc VP8PredLuma4[/* NUM_BMODES */]; extern const VP8PredFunc VP8PredLuma4[/* NUM_BMODES */];
// simple filter (only for luma) // simple filter (only for luma)
typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh); typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh);
@ -124,24 +138,23 @@ extern VP8ChromaFilterFunc VP8VFilter8i; // filtering u and v altogether
extern VP8ChromaFilterFunc VP8HFilter8i; extern VP8ChromaFilterFunc VP8HFilter8i;
// must be called before anything using the above // must be called before anything using the above
extern void VP8DspInit(void); void VP8DspInit(void);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// WebP I/O // WebP I/O
#define FANCY_UPSAMPLING // undefined to remove fancy upsampling support #define FANCY_UPSAMPLING // undefined to remove fancy upsampling support
#ifdef FANCY_UPSAMPLING
typedef void (*WebPUpsampleLinePairFunc)( typedef void (*WebPUpsampleLinePairFunc)(
const uint8_t* top_y, const uint8_t* bottom_y, const uint8_t* top_y, const uint8_t* bottom_y,
const uint8_t* top_u, const uint8_t* top_v, const uint8_t* top_u, const uint8_t* top_v,
const uint8_t* cur_u, const uint8_t* cur_v, const uint8_t* cur_u, const uint8_t* cur_v,
uint8_t* top_dst, uint8_t* bottom_dst, int len); uint8_t* top_dst, uint8_t* bottom_dst, int len);
#ifdef FANCY_UPSAMPLING
// Fancy upsampling functions to convert YUV to RGB(A) modes // Fancy upsampling functions to convert YUV to RGB(A) modes
extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
extern WebPUpsampleLinePairFunc WebPUpsamplersKeepAlpha[/* MODE_LAST */];
// Initializes SSE2 version of the fancy upsamplers. // Initializes SSE2 version of the fancy upsamplers.
void WebPInitUpsamplersSSE2(void); void WebPInitUpsamplersSSE2(void);
@ -156,6 +169,11 @@ typedef void (*WebPSampleLinePairFunc)(
extern const WebPSampleLinePairFunc WebPSamplers[/* MODE_LAST */]; extern const WebPSampleLinePairFunc WebPSamplers[/* MODE_LAST */];
// General function for converting two lines of ARGB or RGBA.
// 'alpha_is_last' should be true if 0xff000000 is stored in memory as
// as 0x00, 0x00, 0x00, 0xff (little endian).
WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last);
// YUV444->RGB converters // YUV444->RGB converters
typedef void (*WebPYUV444Converter)(const uint8_t* y, typedef void (*WebPYUV444Converter)(const uint8_t* y,
const uint8_t* u, const uint8_t* v, const uint8_t* u, const uint8_t* v,
@ -166,6 +184,23 @@ extern const WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */];
// Main function to be called // Main function to be called
void WebPInitUpsamplers(void); void WebPInitUpsamplers(void);
//------------------------------------------------------------------------------
// Pre-multiply planes with alpha values
// Apply alpha pre-multiply on an rgba, bgra or argb plane of size w * h.
// alpha_first should be 0 for argb, 1 for rgba or bgra (where alpha is last).
extern void (*WebPApplyAlphaMultiply)(
uint8_t* rgba, int alpha_first, int w, int h, int stride);
// Same, buf specifically for RGBA4444 format
extern void (*WebPApplyAlphaMultiply4444)(
uint8_t* rgba4444, int w, int h, int stride);
// To be called first before using the above.
void WebPInitPremultiply(void);
void WebPInitPremultiplySSE2(void); // should not be called directly.
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,6 +9,8 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h> // for abs()
#include "./dsp.h"
#include "../enc/vp8enci.h" #include "../enc/vp8enci.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
@ -93,7 +95,7 @@ static void InitTables(void) {
} }
} }
static inline uint8_t clip_8b(int v) { static WEBP_INLINE uint8_t clip_8b(int v) {
return (!(v & ~0xff)) ? v : v < 0 ? 0 : 255; return (!(v & ~0xff)) ? v : v < 0 ? 0 : 255;
} }
@ -107,8 +109,8 @@ static const int kC1 = 20091 + (1 << 16);
static const int kC2 = 35468; static const int kC2 = 35468;
#define MUL(a, b) (((a) * (b)) >> 16) #define MUL(a, b) (((a) * (b)) >> 16)
static inline void ITransformOne(const uint8_t* ref, const int16_t* in, static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in,
uint8_t* dst) { uint8_t* dst) {
int C[4 * 4], *tmp; int C[4 * 4], *tmp;
int i; int i;
tmp = C; tmp = C;
@ -241,14 +243,15 @@ static void FTransformWHT(const int16_t* in, int16_t* out) {
#define DST(x, y) dst[(x) + (y) * BPS] #define DST(x, y) dst[(x) + (y) * BPS]
static inline void Fill(uint8_t* dst, int value, int size) { static WEBP_INLINE void Fill(uint8_t* dst, int value, int size) {
int j; int j;
for (j = 0; j < size; ++j) { for (j = 0; j < size; ++j) {
memset(dst + j * BPS, value, size); memset(dst + j * BPS, value, size);
} }
} }
static inline void VerticalPred(uint8_t* dst, const uint8_t* top, int size) { static WEBP_INLINE void VerticalPred(uint8_t* dst,
const uint8_t* top, int size) {
int j; int j;
if (top) { if (top) {
for (j = 0; j < size; ++j) memcpy(dst + j * BPS, top, size); for (j = 0; j < size; ++j) memcpy(dst + j * BPS, top, size);
@ -257,7 +260,8 @@ static inline void VerticalPred(uint8_t* dst, const uint8_t* top, int size) {
} }
} }
static inline void HorizontalPred(uint8_t* dst, const uint8_t* left, int size) { static WEBP_INLINE void HorizontalPred(uint8_t* dst,
const uint8_t* left, int size) {
if (left) { if (left) {
int j; int j;
for (j = 0; j < size; ++j) { for (j = 0; j < size; ++j) {
@ -268,8 +272,8 @@ static inline void HorizontalPred(uint8_t* dst, const uint8_t* left, int size) {
} }
} }
static inline void TrueMotion(uint8_t* dst, const uint8_t* left, static WEBP_INLINE void TrueMotion(uint8_t* dst, const uint8_t* left,
const uint8_t* top, int size) { const uint8_t* top, int size) {
int y; int y;
if (left) { if (left) {
if (top) { if (top) {
@ -298,9 +302,9 @@ static inline void TrueMotion(uint8_t* dst, const uint8_t* left,
} }
} }
static inline void DCMode(uint8_t* dst, const uint8_t* left, static WEBP_INLINE void DCMode(uint8_t* dst, const uint8_t* left,
const uint8_t* top, const uint8_t* top,
int size, int round, int shift) { int size, int round, int shift) {
int DC = 0; int DC = 0;
int j; int j;
if (top) { if (top) {
@ -543,7 +547,8 @@ static void Intra4Preds(uint8_t* dst, const uint8_t* top) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Metric // Metric
static inline int GetSSE(const uint8_t* a, const uint8_t* b, int w, int h) { static WEBP_INLINE int GetSSE(const uint8_t* a, const uint8_t* b,
int w, int h) {
int count = 0; int count = 0;
int y, x; int y, x;
for (y = 0; y < h; ++y) { for (y = 0; y < h; ++y) {
@ -667,7 +672,7 @@ static int QuantizeBlock(int16_t in[16], int16_t out[16],
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Block copy // Block copy
static inline void Copy(const uint8_t* src, uint8_t* dst, int size) { static WEBP_INLINE void Copy(const uint8_t* src, uint8_t* dst, int size) {
int y; int y;
for (y = 0; y < size; ++y) { for (y = 0; y < size; ++y) {
memcpy(dst, src, size); memcpy(dst, src, size);
@ -677,8 +682,6 @@ static inline void Copy(const uint8_t* src, uint8_t* dst, int size) {
} }
static void Copy4x4(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 4); } static void Copy4x4(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 4); }
static void Copy8x8(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 8); }
static void Copy16x16(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 16); }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Initialization // Initialization
@ -701,8 +704,6 @@ VP8WMetric VP8TDisto4x4;
VP8WMetric VP8TDisto16x16; VP8WMetric VP8TDisto16x16;
VP8QuantizeBlock VP8EncQuantizeBlock; VP8QuantizeBlock VP8EncQuantizeBlock;
VP8BlockCopy VP8Copy4x4; VP8BlockCopy VP8Copy4x4;
VP8BlockCopy VP8Copy8x8;
VP8BlockCopy VP8Copy16x16;
extern void VP8EncDspInitSSE2(void); extern void VP8EncDspInitSSE2(void);
@ -726,12 +727,10 @@ void VP8EncDspInit(void) {
VP8TDisto16x16 = Disto16x16; VP8TDisto16x16 = Disto16x16;
VP8EncQuantizeBlock = QuantizeBlock; VP8EncQuantizeBlock = QuantizeBlock;
VP8Copy4x4 = Copy4x4; VP8Copy4x4 = Copy4x4;
VP8Copy8x8 = Copy8x8;
VP8Copy16x16 = Copy16x16;
// If defined, use CPUInfo() to overwrite some pointers with faster versions. // If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8GetCPUInfo) { if (VP8GetCPUInfo) {
#if defined(__SSE2__) || defined(_MSC_VER) #if defined(WEBP_USE_SSE2)
if (VP8GetCPUInfo(kSSE2)) { if (VP8GetCPUInfo(kSSE2)) {
VP8EncDspInitSSE2(); VP8EncDspInitSSE2();
} }

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,7 +9,10 @@
// //
// Author: Christian Duvivier (cduvivier@google.com) // Author: Christian Duvivier (cduvivier@google.com)
#if defined(__SSE2__) || defined(_MSC_VER) #include "./dsp.h"
#if defined(WEBP_USE_SSE2)
#include <stdlib.h> // for abs()
#include <emmintrin.h> #include <emmintrin.h>
#include "../enc/vp8enci.h" #include "../enc/vp8enci.h"
@ -831,4 +834,4 @@ void VP8EncDspInitSSE2(void) {
} // extern "C" } // extern "C"
#endif #endif
#endif //__SSE2__ #endif // WEBP_USE_SSE2

1138
src/dsp/lossless.c Normal file

File diff suppressed because it is too large Load Diff

82
src/dsp/lossless.h Normal file
View File

@ -0,0 +1,82 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Image transforms and color space conversion methods for lossless decoder.
//
// Authors: Vikas Arora (vikaas.arora@gmail.com)
// Jyrki Alakuijala (jyrki@google.com)
#ifndef WEBP_DSP_LOSSLESS_H_
#define WEBP_DSP_LOSSLESS_H_
#include "../webp/types.h"
#include "../webp/decode.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------
// Image transforms.
struct VP8LTransform; // Defined in dec/vp8li.h.
// Performs inverse transform of data given transform information, start and end
// rows. Transform will be applied to rows [row_start, row_end[.
// The *in and *out pointers refer to source and destination data respectively
// corresponding to the intermediate row (row_start).
void VP8LInverseTransform(const struct VP8LTransform* const transform,
int row_start, int row_end,
const uint32_t* const in, uint32_t* const out);
// Subtracts green from blue and red channels.
void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs);
void VP8LResidualImage(int width, int height, int bits,
uint32_t* const argb, uint32_t* const argb_scratch,
uint32_t* const image);
void VP8LColorSpaceTransform(int width, int height, int bits, int step,
uint32_t* const argb, uint32_t* image);
//------------------------------------------------------------------------------
// Color space conversion.
// Converts from BGRA to other color spaces.
void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels,
WEBP_CSP_MODE out_colorspace, uint8_t* const rgba);
//------------------------------------------------------------------------------
// Misc methods.
// Computes sampled size of 'size' when sampling using 'sampling bits'.
static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size,
uint32_t sampling_bits) {
return (size + (1 << sampling_bits) - 1) >> sampling_bits;
}
// Faster logarithm for integers, with the property of log2(0) == 0.
float VP8LFastLog2(int v);
// Fast calculation of v * log2(v) for integer input.
static WEBP_INLINE float VP8LFastSLog2(int v) { return VP8LFastLog2(v) * v; }
// In-place difference of each component with mod 256.
static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) {
const uint32_t alpha_and_green =
0x00ff00ffu + (a & 0xff00ff00u) - (b & 0xff00ff00u);
const uint32_t red_and_blue =
0xff00ff00u + (a & 0x00ff00ffu) - (b & 0x00ff00ffu);
return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu);
}
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
#endif // WEBP_DSP_LOSSLESS_H_

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -11,7 +11,6 @@
#include "./dsp.h" #include "./dsp.h"
#include "./yuv.h" #include "./yuv.h"
#include "../dec/webpi.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -24,7 +23,6 @@ extern "C" {
// Fancy upsampling functions to convert YUV to RGB // Fancy upsampling functions to convert YUV to RGB
WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST]; WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST];
WebPUpsampleLinePairFunc WebPUpsamplersKeepAlpha[MODE_LAST];
// Given samples laid out in a square as: // Given samples laid out in a square as:
// [a b] // [a b]
@ -101,11 +99,6 @@ UPSAMPLE_FUNC(UpsampleBgraLinePair, VP8YuvToBgra, 4)
UPSAMPLE_FUNC(UpsampleArgbLinePair, VP8YuvToArgb, 4) UPSAMPLE_FUNC(UpsampleArgbLinePair, VP8YuvToArgb, 4)
UPSAMPLE_FUNC(UpsampleRgba4444LinePair, VP8YuvToRgba4444, 2) UPSAMPLE_FUNC(UpsampleRgba4444LinePair, VP8YuvToRgba4444, 2)
UPSAMPLE_FUNC(UpsampleRgb565LinePair, VP8YuvToRgb565, 2) UPSAMPLE_FUNC(UpsampleRgb565LinePair, VP8YuvToRgb565, 2)
// These two don't erase the alpha value
UPSAMPLE_FUNC(UpsampleRgbKeepAlphaLinePair, VP8YuvToRgb, 4)
UPSAMPLE_FUNC(UpsampleBgrKeepAlphaLinePair, VP8YuvToBgr, 4)
UPSAMPLE_FUNC(UpsampleArgbKeepAlphaLinePair, VP8YuvToArgbKeepA, 4)
UPSAMPLE_FUNC(UpsampleRgba4444KeepAlphaLinePair, VP8YuvToRgba4444KeepA, 2)
#undef LOAD_UV #undef LOAD_UV
#undef UPSAMPLE_FUNC #undef UPSAMPLE_FUNC
@ -156,9 +149,55 @@ const WebPSampleLinePairFunc WebPSamplers[MODE_LAST] = {
SampleBgraLinePair, // MODE_BGRA SampleBgraLinePair, // MODE_BGRA
SampleArgbLinePair, // MODE_ARGB SampleArgbLinePair, // MODE_ARGB
SampleRgba4444LinePair, // MODE_RGBA_4444 SampleRgba4444LinePair, // MODE_RGBA_4444
SampleRgb565LinePair // MODE_RGB_565 SampleRgb565LinePair, // MODE_RGB_565
SampleRgbaLinePair, // MODE_rgbA
SampleBgraLinePair, // MODE_bgrA
SampleArgbLinePair, // MODE_Argb
SampleRgba4444LinePair // MODE_rgbA_4444
}; };
//------------------------------------------------------------------------------
#if !defined(FANCY_UPSAMPLING)
#define DUAL_SAMPLE_FUNC(FUNC_NAME, FUNC) \
static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bot_y, \
const uint8_t* top_u, const uint8_t* top_v, \
const uint8_t* bot_u, const uint8_t* bot_v, \
uint8_t* top_dst, uint8_t* bot_dst, int len) { \
const int half_len = len >> 1; \
int x; \
if (top_dst != NULL) { \
for (x = 0; x < half_len; ++x) { \
FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x + 0); \
FUNC(top_y[2 * x + 1], top_u[x], top_v[x], top_dst + 8 * x + 4); \
} \
if (len & 1) FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x); \
} \
if (bot_dst != NULL) { \
for (x = 0; x < half_len; ++x) { \
FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x + 0); \
FUNC(bot_y[2 * x + 1], bot_u[x], bot_v[x], bot_dst + 8 * x + 4); \
} \
if (len & 1) FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x); \
} \
}
DUAL_SAMPLE_FUNC(DualLineSamplerBGRA, VP8YuvToBgra)
DUAL_SAMPLE_FUNC(DualLineSamplerARGB, VP8YuvToArgb)
#undef DUAL_SAMPLE_FUNC
#endif // !FANCY_UPSAMPLING
WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last) {
WebPInitUpsamplers();
VP8YUVInit();
#ifdef FANCY_UPSAMPLING
return WebPUpsamplers[alpha_is_last ? MODE_BGRA : MODE_ARGB];
#else
return (alpha_is_last ? DualLineSamplerBGRA : DualLineSamplerARGB);
#endif
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// YUV444 converter // YUV444 converter
@ -186,9 +225,89 @@ const WebPYUV444Converter WebPYUV444Converters[MODE_LAST] = {
Yuv444ToBgra, // MODE_BGRA Yuv444ToBgra, // MODE_BGRA
Yuv444ToArgb, // MODE_ARGB Yuv444ToArgb, // MODE_ARGB
Yuv444ToRgba4444, // MODE_RGBA_4444 Yuv444ToRgba4444, // MODE_RGBA_4444
Yuv444ToRgb565 // MODE_RGB_565 Yuv444ToRgb565, // MODE_RGB_565
Yuv444ToRgba, // MODE_rgbA
Yuv444ToBgra, // MODE_bgrA
Yuv444ToArgb, // MODE_Argb
Yuv444ToRgba4444 // MODE_rgbA_4444
}; };
//------------------------------------------------------------------------------
// Premultiplied modes
// non dithered-modes
// (x * a * 32897) >> 23 is bit-wise equivalent to (int)(x * a / 255.)
// for all 8bit x or a. For bit-wise equivalence to (int)(x * a / 255. + .5),
// one can use instead: (x * a * 65793 + (1 << 23)) >> 24
#if 1 // (int)(x * a / 255.)
#define MULTIPLIER(a) ((a) * 32897UL)
#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)
#else // (int)(x * a / 255. + .5)
#define MULTIPLIER(a) ((a) * 65793UL)
#define PREMULTIPLY(x, m) (((x) * (m) + (1UL << 23)) >> 24)
#endif
static void ApplyAlphaMultiply(uint8_t* rgba, int alpha_first,
int w, int h, int stride) {
while (h-- > 0) {
uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);
const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);
int i;
for (i = 0; i < w; ++i) {
const uint32_t a = alpha[4 * i];
if (a != 0xff) {
const uint32_t mult = MULTIPLIER(a);
rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);
rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);
rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);
}
}
rgba += stride;
}
}
#undef MULTIPLIER
#undef PREMULTIPLY
// rgbA4444
#define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15
static WEBP_INLINE uint8_t dither_hi(uint8_t x) {
return (x & 0xf0) | (x >> 4);
}
static WEBP_INLINE uint8_t dither_lo(uint8_t x) {
return (x & 0x0f) | (x << 4);
}
static WEBP_INLINE uint8_t multiply(uint8_t x, uint32_t m) {
return (x * m) >> 16;
}
static void ApplyAlphaMultiply4444(uint8_t* rgba4444,
int w, int h, int stride) {
while (h-- > 0) {
int i;
for (i = 0; i < w; ++i) {
const uint8_t a = (rgba4444[2 * i + 1] & 0x0f);
const uint32_t mult = MULTIPLIER(a);
const uint8_t r = multiply(dither_hi(rgba4444[2 * i + 0]), mult);
const uint8_t g = multiply(dither_lo(rgba4444[2 * i + 0]), mult);
const uint8_t b = multiply(dither_hi(rgba4444[2 * i + 1]), mult);
rgba4444[2 * i + 0] = (r & 0xf0) | ((g >> 4) & 0x0f);
rgba4444[2 * i + 1] = (b & 0xf0) | a;
}
rgba4444 += stride;
}
}
#undef MULTIPLIER
void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int)
= ApplyAlphaMultiply;
void (*WebPApplyAlphaMultiply4444)(uint8_t*, int, int, int)
= ApplyAlphaMultiply4444;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Main call // Main call
@ -202,17 +321,9 @@ void WebPInitUpsamplers(void) {
WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair; WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair;
WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair; WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair;
WebPUpsamplersKeepAlpha[MODE_RGB] = UpsampleRgbLinePair;
WebPUpsamplersKeepAlpha[MODE_RGBA] = UpsampleRgbKeepAlphaLinePair;
WebPUpsamplersKeepAlpha[MODE_BGR] = UpsampleBgrLinePair;
WebPUpsamplersKeepAlpha[MODE_BGRA] = UpsampleBgrKeepAlphaLinePair;
WebPUpsamplersKeepAlpha[MODE_ARGB] = UpsampleArgbKeepAlphaLinePair;
WebPUpsamplersKeepAlpha[MODE_RGBA_4444] = UpsampleRgba4444KeepAlphaLinePair;
WebPUpsamplersKeepAlpha[MODE_RGB_565] = UpsampleRgb565LinePair;
// If defined, use CPUInfo() to overwrite some pointers with faster versions. // If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8GetCPUInfo) { if (VP8GetCPUInfo != NULL) {
#if defined(__SSE2__) || defined(_MSC_VER) #if defined(WEBP_USE_SSE2)
if (VP8GetCPUInfo(kSSE2)) { if (VP8GetCPUInfo(kSSE2)) {
WebPInitUpsamplersSSE2(); WebPInitUpsamplersSSE2();
} }
@ -221,6 +332,26 @@ void WebPInitUpsamplers(void) {
#endif // FANCY_UPSAMPLING #endif // FANCY_UPSAMPLING
} }
void WebPInitPremultiply(void) {
WebPApplyAlphaMultiply = ApplyAlphaMultiply;
WebPApplyAlphaMultiply4444 = ApplyAlphaMultiply4444;
#ifdef FANCY_UPSAMPLING
WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair;
WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair;
WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair;
WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair;
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
WebPInitPremultiplySSE2();
}
#endif
}
#endif // FANCY_UPSAMPLING
}
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,14 +9,14 @@
// //
// Author: somnath@google.com (Somnath Banerjee) // Author: somnath@google.com (Somnath Banerjee)
#if defined(__SSE2__) || defined(_MSC_VER) #include "./dsp.h"
#if defined(WEBP_USE_SSE2)
#include <assert.h> #include <assert.h>
#include <emmintrin.h> #include <emmintrin.h>
#include <string.h> #include <string.h>
#include "./dsp.h"
#include "./yuv.h" #include "./yuv.h"
#include "../dec/webpi.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -176,9 +176,6 @@ SSE2_UPSAMPLE_FUNC(UpsampleRgbLinePairSSE2, VP8YuvToRgb, 3)
SSE2_UPSAMPLE_FUNC(UpsampleBgrLinePairSSE2, VP8YuvToBgr, 3) SSE2_UPSAMPLE_FUNC(UpsampleBgrLinePairSSE2, VP8YuvToBgr, 3)
SSE2_UPSAMPLE_FUNC(UpsampleRgbaLinePairSSE2, VP8YuvToRgba, 4) SSE2_UPSAMPLE_FUNC(UpsampleRgbaLinePairSSE2, VP8YuvToRgba, 4)
SSE2_UPSAMPLE_FUNC(UpsampleBgraLinePairSSE2, VP8YuvToBgra, 4) SSE2_UPSAMPLE_FUNC(UpsampleBgraLinePairSSE2, VP8YuvToBgra, 4)
// These two don't erase the alpha value
SSE2_UPSAMPLE_FUNC(UpsampleRgbKeepAlphaLinePairSSE2, VP8YuvToRgb, 4)
SSE2_UPSAMPLE_FUNC(UpsampleBgrKeepAlphaLinePairSSE2, VP8YuvToBgr, 4)
#undef GET_M #undef GET_M
#undef PACK_AND_STORE #undef PACK_AND_STORE
@ -190,26 +187,23 @@ SSE2_UPSAMPLE_FUNC(UpsampleBgrKeepAlphaLinePairSSE2, VP8YuvToBgr, 4)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
extern WebPUpsampleLinePairFunc WebPUpsamplersKeepAlpha[/* MODE_LAST */];
#endif // FANCY_UPSAMPLING
void WebPInitUpsamplersSSE2(void) { void WebPInitUpsamplersSSE2(void) {
#ifdef FANCY_UPSAMPLING
WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePairSSE2; WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePairSSE2;
WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePairSSE2; WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePairSSE2;
WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePairSSE2; WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePairSSE2;
WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePairSSE2; WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePairSSE2;
WebPUpsamplersKeepAlpha[MODE_RGB] = UpsampleRgbLinePairSSE2;
WebPUpsamplersKeepAlpha[MODE_RGBA] = UpsampleRgbKeepAlphaLinePairSSE2;
WebPUpsamplersKeepAlpha[MODE_BGR] = UpsampleBgrLinePairSSE2;
WebPUpsamplersKeepAlpha[MODE_BGRA] = UpsampleBgrKeepAlphaLinePairSSE2;
#endif // FANCY_UPSAMPLING
} }
void WebPInitPremultiplySSE2(void) {
WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePairSSE2;
WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePairSSE2;
}
#endif // FANCY_UPSAMPLING
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif //__SSE2__ || _MSC_VER #endif // WEBP_USE_SSE2

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -24,7 +24,7 @@ uint8_t VP8kClip4Bits[YUV_RANGE_MAX - YUV_RANGE_MIN];
static int done = 0; static int done = 0;
static inline uint8_t clip(int v, int max_value) { static WEBP_INLINE uint8_t clip(int v, int max_value) {
return v < 0 ? 0 : v > max_value ? max_value : v; return v < 0 ? 0 : v > max_value ? max_value : v;
} }

View File

@ -1,18 +1,21 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/ // Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// inline YUV->RGB conversion function // inline YUV<->RGB conversion function
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#ifndef WEBP_DSP_YUV_H_ #ifndef WEBP_DSP_YUV_H_
#define WEBP_DSP_YUV_H_ #define WEBP_DSP_YUV_H_
#include "../webp/decode_vp8.h" #include "../dec/decode_vp8.h"
//------------------------------------------------------------------------------
// YUV -> RGB conversion
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -27,8 +30,8 @@ extern int32_t VP8kVToG[256], VP8kUToG[256];
extern uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN]; extern uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN];
extern uint8_t VP8kClip4Bits[YUV_RANGE_MAX - YUV_RANGE_MIN]; extern uint8_t VP8kClip4Bits[YUV_RANGE_MAX - YUV_RANGE_MIN];
static inline void VP8YuvToRgb(uint8_t y, uint8_t u, uint8_t v, static WEBP_INLINE void VP8YuvToRgb(uint8_t y, uint8_t u, uint8_t v,
uint8_t* const rgb) { uint8_t* const rgb) {
const int r_off = VP8kVToR[v]; const int r_off = VP8kVToR[v];
const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX; const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
const int b_off = VP8kUToB[u]; const int b_off = VP8kUToB[u];
@ -37,8 +40,8 @@ static inline void VP8YuvToRgb(uint8_t y, uint8_t u, uint8_t v,
rgb[2] = VP8kClip[y + b_off - YUV_RANGE_MIN]; rgb[2] = VP8kClip[y + b_off - YUV_RANGE_MIN];
} }
static inline void VP8YuvToRgb565(uint8_t y, uint8_t u, uint8_t v, static WEBP_INLINE void VP8YuvToRgb565(uint8_t y, uint8_t u, uint8_t v,
uint8_t* const rgb) { uint8_t* const rgb) {
const int r_off = VP8kVToR[v]; const int r_off = VP8kVToR[v];
const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX; const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
const int b_off = VP8kUToB[u]; const int b_off = VP8kUToB[u];
@ -48,37 +51,25 @@ static inline void VP8YuvToRgb565(uint8_t y, uint8_t u, uint8_t v,
(VP8kClip[y + b_off - YUV_RANGE_MIN] >> 3)); (VP8kClip[y + b_off - YUV_RANGE_MIN] >> 3));
} }
static inline void VP8YuvToArgbKeepA(uint8_t y, uint8_t u, uint8_t v, static WEBP_INLINE void VP8YuvToArgb(uint8_t y, uint8_t u, uint8_t v,
uint8_t* const argb) { uint8_t* const argb) {
// Don't update Aplha (argb[0]) argb[0] = 0xff;
VP8YuvToRgb(y, u, v, argb + 1); VP8YuvToRgb(y, u, v, argb + 1);
} }
static inline void VP8YuvToArgb(uint8_t y, uint8_t u, uint8_t v, static WEBP_INLINE void VP8YuvToRgba4444(uint8_t y, uint8_t u, uint8_t v,
uint8_t* const argb) {
argb[0] = 0xff;
VP8YuvToArgbKeepA(y, u, v, argb);
}
static inline void VP8YuvToRgba4444KeepA(uint8_t y, uint8_t u, uint8_t v,
uint8_t* const argb) { uint8_t* const argb) {
const int r_off = VP8kVToR[v]; const int r_off = VP8kVToR[v];
const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX; const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
const int b_off = VP8kUToB[u]; const int b_off = VP8kUToB[u];
// Don't update Aplha (last 4 bits of argb[1]) // Don't update alpha (last 4 bits of argb[1])
argb[0] = ((VP8kClip4Bits[y + r_off - YUV_RANGE_MIN] << 4) | argb[0] = ((VP8kClip4Bits[y + r_off - YUV_RANGE_MIN] << 4) |
VP8kClip4Bits[y + g_off - YUV_RANGE_MIN]); VP8kClip4Bits[y + g_off - YUV_RANGE_MIN]);
argb[1] = (argb[1] & 0x0f) | (VP8kClip4Bits[y + b_off - YUV_RANGE_MIN] << 4); argb[1] = 0x0f | (VP8kClip4Bits[y + b_off - YUV_RANGE_MIN] << 4);
} }
static inline void VP8YuvToRgba4444(uint8_t y, uint8_t u, uint8_t v, static WEBP_INLINE void VP8YuvToBgr(uint8_t y, uint8_t u, uint8_t v,
uint8_t* const argb) { uint8_t* const bgr) {
argb[1] = 0x0f;
VP8YuvToRgba4444KeepA(y, u, v, argb);
}
static inline void VP8YuvToBgr(uint8_t y, uint8_t u, uint8_t v,
uint8_t* const bgr) {
const int r_off = VP8kVToR[v]; const int r_off = VP8kVToR[v];
const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX; const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
const int b_off = VP8kUToB[u]; const int b_off = VP8kUToB[u];
@ -87,14 +78,14 @@ static inline void VP8YuvToBgr(uint8_t y, uint8_t u, uint8_t v,
bgr[2] = VP8kClip[y + r_off - YUV_RANGE_MIN]; bgr[2] = VP8kClip[y + r_off - YUV_RANGE_MIN];
} }
static inline void VP8YuvToBgra(uint8_t y, uint8_t u, uint8_t v, static WEBP_INLINE void VP8YuvToBgra(uint8_t y, uint8_t u, uint8_t v,
uint8_t* const bgra) { uint8_t* const bgra) {
VP8YuvToBgr(y, u, v, bgra); VP8YuvToBgr(y, u, v, bgra);
bgra[3] = 0xff; bgra[3] = 0xff;
} }
static inline void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v, static WEBP_INLINE void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v,
uint8_t* const rgba) { uint8_t* const rgba) {
VP8YuvToRgb(y, u, v, rgba); VP8YuvToRgb(y, u, v, rgba);
rgba[3] = 0xff; rgba[3] = 0xff;
} }
@ -102,6 +93,34 @@ static inline void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v,
// Must be called before everything, to initialize the tables. // Must be called before everything, to initialize the tables.
void VP8YUVInit(void); void VP8YUVInit(void);
//------------------------------------------------------------------------------
// RGB -> YUV conversion
// The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
// More information at: http://en.wikipedia.org/wiki/YCbCr
// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
// We use 16bit fixed point operations.
static WEBP_INLINE int VP8ClipUV(int v) {
v = (v + (257 << (YUV_FIX + 2 - 1))) >> (YUV_FIX + 2);
return ((v & ~0xff) == 0) ? v : (v < 0) ? 0 : 255;
}
static WEBP_INLINE int VP8RGBToY(int r, int g, int b) {
const int kRound = (1 << (YUV_FIX - 1)) + (16 << YUV_FIX);
const int luma = 16839 * r + 33059 * g + 6420 * b;
return (luma + kRound) >> YUV_FIX; // no need to clip
}
static WEBP_INLINE int VP8RGBToU(int r, int g, int b) {
return VP8ClipUV(-9719 * r - 19081 * g + 28800 * b);
}
static WEBP_INLINE int VP8RGBToV(int r, int g, int b) {
return VP8ClipUV(+28800 * r - 24116 * g - 4684 * b);
}
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif

View File

@ -1,13 +1,32 @@
AM_CPPFLAGS = -I$(top_srcdir)/src AM_CPPFLAGS = -I$(top_srcdir)/src
libwebpencode_la_SOURCES = analysis.c config.c cost.c cost.h filter.c \
frame.c iterator.c picture.c quant.c \
syntax.c tree.c vp8enci.h webpenc.c alpha.c \
layer.c
libwebpencode_la_LDFLAGS = -version-info 2:0:0 -lm
libwebpencode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
libwebpencodeinclude_HEADERS = ../webp/encode.h ../webp/types.h
libwebpencodeincludedir = $(includedir)/webp
noinst_HEADERS = cost.h vp8enci.h
noinst_LTLIBRARIES = libwebpencode.la noinst_LTLIBRARIES = libwebpencode.la
libwebpencode_la_SOURCES =
libwebpencode_la_SOURCES += alpha.c
libwebpencode_la_SOURCES += analysis.c
libwebpencode_la_SOURCES += backward_references.c
libwebpencode_la_SOURCES += config.c
libwebpencode_la_SOURCES += cost.c
libwebpencode_la_SOURCES += cost.h
libwebpencode_la_SOURCES += filter.c
libwebpencode_la_SOURCES += frame.c
libwebpencode_la_SOURCES += histogram.c
libwebpencode_la_SOURCES += iterator.c
libwebpencode_la_SOURCES += layer.c
libwebpencode_la_SOURCES += picture.c
libwebpencode_la_SOURCES += quant.c
libwebpencode_la_SOURCES += syntax.c
libwebpencode_la_SOURCES += tree.c
libwebpencode_la_SOURCES += vp8enci.h
libwebpencode_la_SOURCES += vp8l.c
libwebpencode_la_SOURCES += webpenc.c
libwebpencodeinclude_HEADERS =
libwebpencodeinclude_HEADERS += ../webp/encode.h
libwebpencodeinclude_HEADERS += ../webp/types.h
noinst_HEADERS =
noinst_HEADERS += ../webp/format_constants.h
libwebpencode_la_LDFLAGS = -lm
libwebpencode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
libwebpencodeincludedir = $(includedir)/webp

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -11,98 +11,314 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include "vp8enci.h"
#ifdef WEBP_EXPERIMENTAL_FEATURES #include "./vp8enci.h"
#include "zlib.h" #include "../utils/filters.h"
#endif #include "../utils/quant_levels.h"
#include "../webp/format_constants.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
#ifdef WEBP_EXPERIMENTAL_FEATURES // -----------------------------------------------------------------------------
// Encodes the given alpha data via specified compression method 'method'.
// The pre-processing (quantization) is performed if 'quality' is less than 100.
// For such cases, the encoding is lossy. The valid range is [0, 100] for
// 'quality' and [0, 1] for 'method':
// 'method = 0' - No compression;
// 'method = 1' - Use lossless coder on the alpha plane only
// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
// vertical & gradient filters. The prediction mode 4 will try all the
// prediction modes 0 to 3 and pick the best one.
// 'effort_level': specifies how much effort must be spent to try and reduce
// the compressed output size. In range 0 (quick) to 6 (slow).
//
// 'output' corresponds to the buffer containing compressed alpha data.
// This buffer is allocated by this method and caller should call
// free(*output) when done.
// 'output_size' corresponds to size of this compressed alpha buffer.
//
// Returns 1 on successfully encoding the alpha and
// 0 if either:
// invalid quality or method, or
// memory allocation for the compressed data fails.
#define CHUNK_SIZE 8192 #include "../enc/vp8li.h"
//------------------------------------------------------------------------------ static int EncodeLossless(const uint8_t* const data, int width, int height,
int effort_level, // in [0..6] range
VP8BitWriter* const bw,
WebPAuxStats* const stats) {
int ok = 0;
WebPConfig config;
WebPPicture picture;
VP8LBitWriter tmp_bw;
static int CompressAlpha(const uint8_t* data, size_t data_size, WebPPictureInit(&picture);
uint8_t** output, size_t* output_size, picture.width = width;
int algo) { picture.height = height;
int ret = Z_OK; picture.use_argb = 1;
z_stream strm; picture.stats = stats;
unsigned char chunk[CHUNK_SIZE]; if (!WebPPictureAlloc(&picture)) return 0;
*output = NULL; // Transfer the alpha values to the green channel.
*output_size = 0; {
memset(&strm, 0, sizeof(strm)); int i, j;
if (deflateInit(&strm, algo ? Z_BEST_SPEED : Z_BEST_COMPRESSION) != Z_OK) { uint32_t* dst = picture.argb;
return 0; const uint8_t* src = data;
} for (j = 0; j < picture.height; ++j) {
strm.next_in = (unsigned char*)data; for (i = 0; i < picture.width; ++i) {
strm.avail_in = data_size; dst[i] = (src[i] << 8) | 0xff000000u;
do {
size_t size_out;
strm.next_out = chunk;
strm.avail_out = CHUNK_SIZE;
ret = deflate(&strm, Z_FINISH);
if (ret == Z_STREAM_ERROR) {
break;
}
size_out = CHUNK_SIZE - strm.avail_out;
if (size_out) {
size_t new_size = *output_size + size_out;
uint8_t* new_output = realloc(*output, new_size);
if (new_output == NULL) {
ret = Z_MEM_ERROR;
break;
} }
memcpy(new_output + *output_size, chunk, size_out); src += width;
*output_size = new_size; dst += picture.argb_stride;
*output = new_output;
} }
} while (ret != Z_STREAM_END || strm.avail_out == 0);
deflateEnd(&strm);
if (ret != Z_STREAM_END) {
free(*output);
output_size = 0;
return 0;
} }
return 1;
WebPConfigInit(&config);
config.lossless = 1;
config.method = effort_level; // impact is very small
// Set moderate default quality setting for alpha. Higher qualities (80 and
// above) could be very slow.
config.quality = 10.f + 15.f * effort_level;
if (config.quality > 100.f) config.quality = 100.f;
ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3);
ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK);
WebPPictureFree(&picture);
if (ok) {
const uint8_t* const data = VP8LBitWriterFinish(&tmp_bw);
const size_t data_size = VP8LBitWriterNumBytes(&tmp_bw);
VP8BitWriterAppend(bw, data, data_size);
}
VP8LBitWriterDestroy(&tmp_bw);
return ok && !bw->error_;
} }
#endif /* WEBP_EXPERIMENTAL_FEATURES */ // -----------------------------------------------------------------------------
void VP8EncInitAlpha(VP8Encoder* enc) { static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
enc->has_alpha_ = (enc->pic_->a != NULL); int method, int filter, int reduce_levels,
int effort_level, // in [0..6] range
uint8_t* const tmp_alpha,
VP8BitWriter* const bw,
WebPAuxStats* const stats) {
int ok = 0;
const uint8_t* alpha_src;
WebPFilterFunc filter_func;
uint8_t header;
size_t expected_size;
const size_t data_size = width * height;
assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
assert(filter >= 0 && filter < WEBP_FILTER_LAST);
assert(method >= ALPHA_NO_COMPRESSION);
assert(method <= ALPHA_LOSSLESS_COMPRESSION);
assert(sizeof(header) == ALPHA_HEADER_LEN);
// TODO(skal): have a common function and #define's to validate alpha params.
expected_size =
(method == ALPHA_NO_COMPRESSION) ? (ALPHA_HEADER_LEN + data_size)
: (data_size >> 5);
header = method | (filter << 2);
if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
VP8BitWriterInit(bw, expected_size);
VP8BitWriterAppend(bw, &header, ALPHA_HEADER_LEN);
filter_func = WebPFilters[filter];
if (filter_func) {
filter_func(data, width, height, 1, width, tmp_alpha);
alpha_src = tmp_alpha;
} else {
alpha_src = data;
}
if (method == ALPHA_NO_COMPRESSION) {
ok = VP8BitWriterAppend(bw, alpha_src, width * height);
ok = ok && !bw->error_;
} else {
ok = EncodeLossless(alpha_src, width, height, effort_level, bw, stats);
VP8BitWriterFinish(bw);
}
return ok;
}
// -----------------------------------------------------------------------------
// TODO(skal): move to dsp/ ?
static void CopyPlane(const uint8_t* src, int src_stride,
uint8_t* dst, int dst_stride, int width, int height) {
while (height-- > 0) {
memcpy(dst, src, width);
src += src_stride;
dst += dst_stride;
}
}
static int EncodeAlpha(VP8Encoder* const enc,
int quality, int method, int filter,
int effort_level,
uint8_t** const output, size_t* const output_size) {
const WebPPicture* const pic = enc->pic_;
const int width = pic->width;
const int height = pic->height;
uint8_t* quant_alpha = NULL;
const size_t data_size = width * height;
uint64_t sse = 0;
int ok = 1;
const int reduce_levels = (quality < 100);
// quick sanity checks
assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
assert(enc != NULL && pic != NULL && pic->a != NULL);
assert(output != NULL && output_size != NULL);
assert(width > 0 && height > 0);
assert(pic->a_stride >= width);
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
if (quality < 0 || quality > 100) {
return 0;
}
if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
return 0;
}
quant_alpha = (uint8_t*)malloc(data_size);
if (quant_alpha == NULL) {
return 0;
}
// Extract alpha data (width x height) from raw_data (stride x height).
CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
if (reduce_levels) { // No Quantization required for 'quality = 100'.
// 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
// mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16]
// and Quality:]70, 100] -> Levels:]16, 256].
const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
: (16 + (quality - 70) * 8);
ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse);
}
if (ok) {
VP8BitWriter bw;
int test_filter;
uint8_t* filtered_alpha = NULL;
// We always test WEBP_FILTER_NONE first.
ok = EncodeAlphaInternal(quant_alpha, width, height,
method, WEBP_FILTER_NONE, reduce_levels,
effort_level, NULL, &bw, pic->stats);
if (!ok) {
VP8BitWriterWipeOut(&bw);
goto End;
}
if (filter == WEBP_FILTER_FAST) { // Quick estimate of a second candidate?
filter = EstimateBestFilter(quant_alpha, width, height, width);
}
// Stop?
if (filter == WEBP_FILTER_NONE) {
goto Ok;
}
filtered_alpha = (uint8_t*)malloc(data_size);
ok = (filtered_alpha != NULL);
if (!ok) {
goto End;
}
// Try the other mode(s).
{
WebPAuxStats best_stats;
size_t best_score = VP8BitWriterSize(&bw);
memset(&best_stats, 0, sizeof(best_stats)); // prevent spurious warning
if (pic->stats != NULL) best_stats = *pic->stats;
for (test_filter = WEBP_FILTER_HORIZONTAL;
ok && (test_filter <= WEBP_FILTER_GRADIENT);
++test_filter) {
VP8BitWriter tmp_bw;
if (filter != WEBP_FILTER_BEST && test_filter != filter) {
continue;
}
ok = EncodeAlphaInternal(quant_alpha, width, height,
method, test_filter, reduce_levels,
effort_level, filtered_alpha, &tmp_bw,
pic->stats);
if (ok) {
const size_t score = VP8BitWriterSize(&tmp_bw);
if (score < best_score) {
// swap bitwriter objects.
VP8BitWriter tmp = tmp_bw;
tmp_bw = bw;
bw = tmp;
best_score = score;
if (pic->stats != NULL) best_stats = *pic->stats;
}
} else {
VP8BitWriterWipeOut(&bw);
}
VP8BitWriterWipeOut(&tmp_bw);
}
if (pic->stats != NULL) *pic->stats = best_stats;
}
Ok:
if (ok) {
*output_size = VP8BitWriterSize(&bw);
*output = VP8BitWriterBuf(&bw);
if (pic->stats != NULL) { // need stats?
pic->stats->coded_size += (int)(*output_size);
enc->sse_[3] = sse;
}
}
free(filtered_alpha);
}
End:
free(quant_alpha);
return ok;
}
//------------------------------------------------------------------------------
// Main calls
void VP8EncInitAlpha(VP8Encoder* const enc) {
enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
enc->alpha_data_ = NULL; enc->alpha_data_ = NULL;
enc->alpha_data_size_ = 0; enc->alpha_data_size_ = 0;
} }
void VP8EncCodeAlphaBlock(VP8EncIterator* it) { int VP8EncFinishAlpha(VP8Encoder* const enc) {
(void)it;
// Nothing for now. We just ZLIB-compress in the end.
}
int VP8EncFinishAlpha(VP8Encoder* enc) {
if (enc->has_alpha_) { if (enc->has_alpha_) {
#ifdef WEBP_EXPERIMENTAL_FEATURES const WebPConfig* config = enc->config_;
const WebPPicture* pic = enc->pic_; uint8_t* tmp_data = NULL;
assert(pic->a); size_t tmp_size = 0;
if (!CompressAlpha(pic->a, pic->width * pic->height, const int effort_level = config->method; // maps to [0..6]
&enc->alpha_data_, &enc->alpha_data_size_, const WEBP_FILTER_TYPE filter =
enc->config_->alpha_compression)) { (config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
(config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
WEBP_FILTER_BEST;
if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
filter, effort_level, &tmp_data, &tmp_size)) {
return 0; return 0;
} }
#endif if (tmp_size != (uint32_t)tmp_size) { // Sanity check.
free(tmp_data);
return 0;
}
enc->alpha_data_size_ = (uint32_t)tmp_size;
enc->alpha_data_ = tmp_data;
} }
return 1; return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
} }
void VP8EncDeleteAlpha(VP8Encoder* enc) { void VP8EncDeleteAlpha(VP8Encoder* const enc) {
free(enc->alpha_data_); free(enc->alpha_data_);
enc->alpha_data_ = NULL; enc->alpha_data_ = NULL;
enc->alpha_data_size_ = 0; enc->alpha_data_size_ = 0;

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -13,8 +13,9 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "vp8enci.h" #include "./vp8enci.h"
#include "cost.h" #include "./cost.h"
#include "../utils/utils.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -35,7 +36,8 @@ static void SmoothSegmentMap(VP8Encoder* const enc) {
const int w = enc->mb_w_; const int w = enc->mb_w_;
const int h = enc->mb_h_; const int h = enc->mb_h_;
const int majority_cnt_3_x_3_grid = 5; const int majority_cnt_3_x_3_grid = 5;
uint8_t* tmp = (uint8_t*)malloc(w * h * sizeof(uint8_t)); uint8_t* const tmp = (uint8_t*)WebPSafeMalloc((uint64_t)w * h, sizeof(*tmp));
assert((uint64_t)(w * h) == (uint64_t)w * h); // no overflow, as per spec
if (tmp == NULL) return; if (tmp == NULL) return;
for (y = 1; y < h - 1; ++y) { for (y = 1; y < h - 1; ++y) {
@ -112,7 +114,7 @@ static void SetSegmentProbas(VP8Encoder* const enc) {
} }
} }
static inline int clip(int v, int m, int M) { static WEBP_INLINE int clip(int v, int m, int M) {
return v < m ? m : v > M ? M : v; return v < m ? m : v > M ? M : v;
} }
@ -145,7 +147,7 @@ static void SetSegmentAlphas(VP8Encoder* const enc,
static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) { static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) {
const int nb = enc->segment_hdr_.num_segments_; const int nb = enc->segment_hdr_.num_segments_;
int centers[NUM_MB_SEGMENTS]; int centers[NUM_MB_SEGMENTS];
int weighted_average; int weighted_average = 0;
int map[256]; int map[256];
int a, n, k; int a, n, k;
int min_a = 0, max_a = 255, range_a; int min_a = 0, max_a = 255, range_a;
@ -206,9 +208,9 @@ static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) {
// Map each original value to the closest centroid // Map each original value to the closest centroid
for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
VP8MBInfo* const mb = &enc->mb_info_[n]; VP8MBInfo* const mb = &enc->mb_info_[n];
const int a = mb->alpha_; const int alpha = mb->alpha_;
mb->segment_ = map[a]; mb->segment_ = map[alpha];
mb->alpha_ = centers[map[a]]; // just for the record. mb->alpha_ = centers[map[alpha]]; // just for the record.
} }
if (nb > 1) { if (nb > 1) {
@ -253,7 +255,7 @@ static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) {
static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it, static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it,
int best_alpha) { int best_alpha) {
int modes[16]; uint8_t modes[16];
const int max_mode = (it->enc_->method_ >= 3) ? MAX_INTRA4_MODE : NUM_BMODES; const int max_mode = (it->enc_->method_ >= 3) ? MAX_INTRA4_MODE : NUM_BMODES;
int i4_alpha = 0; int i4_alpha = 0;
VP8IteratorStartI4(it); VP8IteratorStartI4(it);
@ -339,6 +341,7 @@ static void MBAnalyze(VP8EncIterator* const it,
// this stage. // this stage.
int VP8EncAnalyze(VP8Encoder* const enc) { int VP8EncAnalyze(VP8Encoder* const enc) {
int ok = 1;
int alphas[256] = { 0 }; int alphas[256] = { 0 };
VP8EncIterator it; VP8EncIterator it;
@ -347,12 +350,13 @@ int VP8EncAnalyze(VP8Encoder* const enc) {
do { do {
VP8IteratorImport(&it); VP8IteratorImport(&it);
MBAnalyze(&it, alphas, &enc->uv_alpha_); MBAnalyze(&it, alphas, &enc->uv_alpha_);
ok = VP8IteratorProgress(&it, 20);
// Let's pretend we have perfect lossless reconstruction. // Let's pretend we have perfect lossless reconstruction.
} while (VP8IteratorNext(&it, it.yuv_in_)); } while (ok && VP8IteratorNext(&it, it.yuv_in_));
enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_; enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_;
AssignSegments(enc, alphas); if (ok) AssignSegments(enc, alphas);
return 1; return ok;
} }
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)

View File

@ -0,0 +1,874 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Author: Jyrki Alakuijala (jyrki@google.com)
//
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include "./backward_references.h"
#include "./histogram.h"
#include "../dsp/lossless.h"
#include "../utils/color_cache.h"
#include "../utils/utils.h"
#define VALUES_IN_BYTE 256
#define HASH_BITS 18
#define HASH_SIZE (1 << HASH_BITS)
#define HASH_MULTIPLIER (0xc6a4a7935bd1e995ULL)
// 1M window (4M bytes) minus 120 special codes for short distances.
#define WINDOW_SIZE ((1 << 20) - 120)
// Bounds for the match length.
#define MIN_LENGTH 2
#define MAX_LENGTH 4096
typedef struct {
// Stores the most recently added position with the given hash value.
int32_t hash_to_first_index_[HASH_SIZE];
// chain_[pos] stores the previous position with the same hash value
// for every pixel in the image.
int32_t* chain_;
} HashChain;
// -----------------------------------------------------------------------------
static const uint8_t plane_to_code_lut[128] = {
96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255,
101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79,
102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87,
105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91,
110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100,
115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109,
118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114,
119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117
};
static int DistanceToPlaneCode(int xsize, int dist) {
const int yoffset = dist / xsize;
const int xoffset = dist - yoffset * xsize;
if (xoffset <= 8 && yoffset < 8) {
return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;
} else if (xoffset > xsize - 8 && yoffset < 7) {
return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;
}
return dist + 120;
}
static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,
const uint32_t* const array2,
const int max_limit) {
int match_len = 0;
while (match_len < max_limit && array1[match_len] == array2[match_len]) {
++match_len;
}
return match_len;
}
// -----------------------------------------------------------------------------
// VP8LBackwardRefs
void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) {
if (refs != NULL) {
refs->refs = NULL;
refs->size = 0;
refs->max_size = 0;
}
}
void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) {
if (refs != NULL) {
free(refs->refs);
VP8LInitBackwardRefs(refs);
}
}
int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size) {
assert(refs != NULL);
refs->size = 0;
refs->max_size = 0;
refs->refs = (PixOrCopy*)WebPSafeMalloc((uint64_t)max_size,
sizeof(*refs->refs));
if (refs->refs == NULL) return 0;
refs->max_size = max_size;
return 1;
}
// -----------------------------------------------------------------------------
// Hash chains
static WEBP_INLINE uint64_t GetPixPairHash64(const uint32_t* const argb) {
uint64_t key = ((uint64_t)(argb[1]) << 32) | argb[0];
key = (key * HASH_MULTIPLIER) >> (64 - HASH_BITS);
return key;
}
static int HashChainInit(HashChain* const p, int size) {
int i;
p->chain_ = (int*)WebPSafeMalloc((uint64_t)size, sizeof(*p->chain_));
if (p->chain_ == NULL) {
return 0;
}
for (i = 0; i < size; ++i) {
p->chain_[i] = -1;
}
for (i = 0; i < HASH_SIZE; ++i) {
p->hash_to_first_index_[i] = -1;
}
return 1;
}
static void HashChainDelete(HashChain* const p) {
if (p != NULL) {
free(p->chain_);
free(p);
}
}
// Insertion of two pixels at a time.
static void HashChainInsert(HashChain* const p,
const uint32_t* const argb, int pos) {
const uint64_t hash_code = GetPixPairHash64(argb);
p->chain_[pos] = p->hash_to_first_index_[hash_code];
p->hash_to_first_index_[hash_code] = pos;
}
static int HashChainFindCopy(const HashChain* const p,
int quality, int index, int xsize,
const uint32_t* const argb, int maxlen,
int* const distance_ptr,
int* const length_ptr) {
const uint64_t hash_code = GetPixPairHash64(&argb[index]);
int prev_length = 0;
int64_t best_val = 0;
int best_length = 0;
int best_distance = 0;
const uint32_t* const argb_start = argb + index;
const int iter_min_mult = (quality < 50) ? 2 : (quality < 75) ? 4 : 8;
const int iter_min = -quality * iter_min_mult;
int iter_cnt = 10 + (quality >> 1);
const int min_pos = (index > WINDOW_SIZE) ? index - WINDOW_SIZE : 0;
int pos;
assert(xsize > 0);
for (pos = p->hash_to_first_index_[hash_code];
pos >= min_pos;
pos = p->chain_[pos]) {
int64_t val;
int curr_length;
if (iter_cnt < 0) {
if (iter_cnt < iter_min || best_val >= 0xff0000) {
break;
}
}
--iter_cnt;
if (best_length != 0 &&
argb[pos + best_length - 1] != argb_start[best_length - 1]) {
continue;
}
curr_length = FindMatchLength(argb + pos, argb_start, maxlen);
if (curr_length < prev_length) {
continue;
}
val = 65536 * curr_length;
// Favoring 2d locality here gives savings for certain images.
if (index - pos < 9 * xsize) {
const int y = (index - pos) / xsize;
int x = (index - pos) % xsize;
if (x > xsize / 2) {
x = xsize - x;
}
if (x <= 7 && x >= -8) {
val -= y * y + x * x;
} else {
val -= 9 * 9 + 9 * 9;
}
} else {
val -= 9 * 9 + 9 * 9;
}
if (best_val < val) {
prev_length = curr_length;
best_val = val;
best_length = curr_length;
best_distance = index - pos;
if (curr_length >= MAX_LENGTH) {
break;
}
if ((best_distance == 1 || best_distance == xsize) &&
best_length >= 128) {
break;
}
}
}
*distance_ptr = best_distance;
*length_ptr = best_length;
return (best_length >= MIN_LENGTH);
}
static WEBP_INLINE void PushBackCopy(VP8LBackwardRefs* const refs, int length) {
int size = refs->size;
while (length >= MAX_LENGTH) {
refs->refs[size++] = PixOrCopyCreateCopy(1, MAX_LENGTH);
length -= MAX_LENGTH;
}
if (length > 0) {
refs->refs[size++] = PixOrCopyCreateCopy(1, length);
}
refs->size = size;
}
static void BackwardReferencesRle(int xsize, int ysize,
const uint32_t* const argb,
VP8LBackwardRefs* const refs) {
const int pix_count = xsize * ysize;
int match_len = 0;
int i;
refs->size = 0;
PushBackCopy(refs, match_len); // i=0 case
refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[0]);
for (i = 1; i < pix_count; ++i) {
if (argb[i] == argb[i - 1]) {
++match_len;
} else {
PushBackCopy(refs, match_len);
match_len = 0;
refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[i]);
}
}
PushBackCopy(refs, match_len);
}
static int BackwardReferencesHashChain(int xsize, int ysize,
const uint32_t* const argb,
int cache_bits, int quality,
VP8LBackwardRefs* const refs) {
int i;
int ok = 0;
int cc_init = 0;
const int use_color_cache = (cache_bits > 0);
const int pix_count = xsize * ysize;
HashChain* const hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
VP8LColorCache hashers;
if (hash_chain == NULL) return 0;
if (use_color_cache) {
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
if (!cc_init) goto Error;
}
if (!HashChainInit(hash_chain, pix_count)) goto Error;
refs->size = 0;
for (i = 0; i < pix_count; ) {
// Alternative#1: Code the pixels starting at 'i' using backward reference.
int offset = 0;
int len = 0;
if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1].
int maxlen = pix_count - i;
if (maxlen > MAX_LENGTH) {
maxlen = MAX_LENGTH;
}
HashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen,
&offset, &len);
}
if (len >= MIN_LENGTH) {
// Alternative#2: Insert the pixel at 'i' as literal, and code the
// pixels starting at 'i + 1' using backward reference.
int offset2 = 0;
int len2 = 0;
int k;
HashChainInsert(hash_chain, &argb[i], i);
if (i < pix_count - 2) { // FindCopy(i+1,..) reads [i + 1] and [i + 2].
int maxlen = pix_count - (i + 1);
if (maxlen > MAX_LENGTH) {
maxlen = MAX_LENGTH;
}
HashChainFindCopy(hash_chain, quality,
i + 1, xsize, argb, maxlen, &offset2, &len2);
if (len2 > len + 1) {
const uint32_t pixel = argb[i];
// Alternative#2 is a better match. So push pixel at 'i' as literal.
if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
} else {
refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel);
}
++refs->size;
if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
i++; // Backward reference to be done for next pixel.
len = len2;
offset = offset2;
}
}
if (len >= MAX_LENGTH) {
len = MAX_LENGTH - 1;
}
refs->refs[refs->size++] = PixOrCopyCreateCopy(offset, len);
if (use_color_cache) {
for (k = 0; k < len; ++k) {
VP8LColorCacheInsert(&hashers, argb[i + k]);
}
}
// Add to the hash_chain (but cannot add the last pixel).
{
const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
for (k = 1; k < last; ++k) {
HashChainInsert(hash_chain, &argb[i + k], i + k);
}
}
i += len;
} else {
const uint32_t pixel = argb[i];
if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
// push pixel as a PixOrCopyCreateCacheIdx pixel
const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
} else {
refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel);
}
++refs->size;
if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
if (i + 1 < pix_count) {
HashChainInsert(hash_chain, &argb[i], i);
}
++i;
}
}
ok = 1;
Error:
if (cc_init) VP8LColorCacheClear(&hashers);
HashChainDelete(hash_chain);
return ok;
}
// -----------------------------------------------------------------------------
typedef struct {
double alpha_[VALUES_IN_BYTE];
double red_[VALUES_IN_BYTE];
double literal_[PIX_OR_COPY_CODES_MAX];
double blue_[VALUES_IN_BYTE];
double distance_[NUM_DISTANCE_CODES];
} CostModel;
static int BackwardReferencesTraceBackwards(
int xsize, int ysize, int recursive_cost_model,
const uint32_t* const argb, int cache_bits, VP8LBackwardRefs* const refs);
static void ConvertPopulationCountTableToBitEstimates(
int num_symbols, const int population_counts[], double output[]) {
int sum = 0;
int nonzeros = 0;
int i;
for (i = 0; i < num_symbols; ++i) {
sum += population_counts[i];
if (population_counts[i] > 0) {
++nonzeros;
}
}
if (nonzeros <= 1) {
memset(output, 0, num_symbols * sizeof(*output));
} else {
const double logsum = VP8LFastLog2(sum);
for (i = 0; i < num_symbols; ++i) {
output[i] = logsum - VP8LFastLog2(population_counts[i]);
}
}
}
static int CostModelBuild(CostModel* const m, int xsize, int ysize,
int recursion_level, const uint32_t* const argb,
int cache_bits) {
int ok = 0;
VP8LHistogram histo;
VP8LBackwardRefs refs;
const int quality = 100;
if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize)) goto Error;
if (recursion_level > 0) {
if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1,
argb, cache_bits, &refs)) {
goto Error;
}
} else {
if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality,
&refs)) {
goto Error;
}
}
VP8LHistogramCreate(&histo, &refs, cache_bits);
ConvertPopulationCountTableToBitEstimates(
VP8LHistogramNumCodes(&histo), histo.literal_, m->literal_);
ConvertPopulationCountTableToBitEstimates(
VALUES_IN_BYTE, histo.red_, m->red_);
ConvertPopulationCountTableToBitEstimates(
VALUES_IN_BYTE, histo.blue_, m->blue_);
ConvertPopulationCountTableToBitEstimates(
VALUES_IN_BYTE, histo.alpha_, m->alpha_);
ConvertPopulationCountTableToBitEstimates(
NUM_DISTANCE_CODES, histo.distance_, m->distance_);
ok = 1;
Error:
VP8LClearBackwardRefs(&refs);
return ok;
}
static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {
return m->alpha_[v >> 24] +
m->red_[(v >> 16) & 0xff] +
m->literal_[(v >> 8) & 0xff] +
m->blue_[v & 0xff];
}
static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) {
const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
return m->literal_[literal_idx];
}
static WEBP_INLINE double GetLengthCost(const CostModel* const m,
uint32_t length) {
int code, extra_bits_count, extra_bits_value;
PrefixEncode(length, &code, &extra_bits_count, &extra_bits_value);
return m->literal_[VALUES_IN_BYTE + code] + extra_bits_count;
}
static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
uint32_t distance) {
int code, extra_bits_count, extra_bits_value;
PrefixEncode(distance, &code, &extra_bits_count, &extra_bits_value);
return m->distance_[code] + extra_bits_count;
}
static int BackwardReferencesHashChainDistanceOnly(
int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb,
int cache_bits, uint32_t* const dist_array) {
int i;
int ok = 0;
int cc_init = 0;
const int quality = 100;
const int pix_count = xsize * ysize;
const int use_color_cache = (cache_bits > 0);
double* const cost =
(double*)WebPSafeMalloc((uint64_t)pix_count, sizeof(*cost));
CostModel* cost_model = (CostModel*)malloc(sizeof(*cost_model));
HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
VP8LColorCache hashers;
const double mul0 = (recursive_cost_model != 0) ? 1.0 : 0.68;
const double mul1 = (recursive_cost_model != 0) ? 1.0 : 0.82;
if (cost == NULL || cost_model == NULL || hash_chain == NULL) goto Error;
if (!HashChainInit(hash_chain, pix_count)) goto Error;
if (use_color_cache) {
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
if (!cc_init) goto Error;
}
if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb,
cache_bits)) {
goto Error;
}
for (i = 0; i < pix_count; ++i) cost[i] = 1e100;
// We loop one pixel at a time, but store all currently best points to
// non-processed locations from this point.
dist_array[0] = 0;
for (i = 0; i < pix_count; ++i) {
double prev_cost = 0.0;
int shortmax;
if (i > 0) {
prev_cost = cost[i - 1];
}
for (shortmax = 0; shortmax < 2; ++shortmax) {
int offset = 0;
int len = 0;
if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1].
int maxlen = shortmax ? 2 : MAX_LENGTH;
if (maxlen > pix_count - i) {
maxlen = pix_count - i;
}
HashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen,
&offset, &len);
}
if (len >= MIN_LENGTH) {
const int code = DistanceToPlaneCode(xsize, offset);
const double distance_cost =
prev_cost + GetDistanceCost(cost_model, code);
int k;
for (k = 1; k < len; ++k) {
const double cost_val =
distance_cost + GetLengthCost(cost_model, k);
if (cost[i + k] > cost_val) {
cost[i + k] = cost_val;
dist_array[i + k] = k + 1;
}
}
// This if is for speedup only. It roughly doubles the speed, and
// makes compression worse by .1 %.
if (len >= 128 && code < 2) {
// Long copy for short distances, let's skip the middle
// lookups for better copies.
// 1) insert the hashes.
if (use_color_cache) {
for (k = 0; k < len; ++k) {
VP8LColorCacheInsert(&hashers, argb[i + k]);
}
}
// 2) Add to the hash_chain (but cannot add the last pixel)
{
const int last = (len < pix_count - 1 - i) ? len
: pix_count - 1 - i;
for (k = 0; k < last; ++k) {
HashChainInsert(hash_chain, &argb[i + k], i + k);
}
}
// 3) jump.
i += len - 1; // for loop does ++i, thus -1 here.
goto next_symbol;
}
}
}
if (i < pix_count - 1) {
HashChainInsert(hash_chain, &argb[i], i);
}
{
// inserting a literal pixel
double cost_val = prev_cost;
if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]);
cost_val += GetCacheCost(cost_model, ix) * mul0;
} else {
cost_val += GetLiteralCost(cost_model, argb[i]) * mul1;
}
if (cost[i] > cost_val) {
cost[i] = cost_val;
dist_array[i] = 1; // only one is inserted.
}
if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
}
next_symbol: ;
}
// Last pixel still to do, it can only be a single step if not reached
// through cheaper means already.
ok = 1;
Error:
if (cc_init) VP8LColorCacheClear(&hashers);
HashChainDelete(hash_chain);
free(cost_model);
free(cost);
return ok;
}
static int TraceBackwards(const uint32_t* const dist_array,
int dist_array_size,
uint32_t** const chosen_path,
int* const chosen_path_size) {
int i;
// Count how many.
int count = 0;
for (i = dist_array_size - 1; i >= 0; ) {
int k = dist_array[i];
assert(k >= 1);
++count;
i -= k;
}
// Allocate.
*chosen_path_size = count;
*chosen_path =
(uint32_t*)WebPSafeMalloc((uint64_t)count, sizeof(**chosen_path));
if (*chosen_path == NULL) return 0;
// Write in reverse order.
for (i = dist_array_size - 1; i >= 0; ) {
int k = dist_array[i];
assert(k >= 1);
(*chosen_path)[--count] = k;
i -= k;
}
return 1;
}
static int BackwardReferencesHashChainFollowChosenPath(
int xsize, int ysize, const uint32_t* const argb, int cache_bits,
const uint32_t* const chosen_path, int chosen_path_size,
VP8LBackwardRefs* const refs) {
const int quality = 100;
const int pix_count = xsize * ysize;
const int use_color_cache = (cache_bits > 0);
int size = 0;
int i = 0;
int k;
int ix;
int ok = 0;
int cc_init = 0;
HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
VP8LColorCache hashers;
if (hash_chain == NULL || !HashChainInit(hash_chain, pix_count)) {
goto Error;
}
if (use_color_cache) {
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
if (!cc_init) goto Error;
}
refs->size = 0;
for (ix = 0; ix < chosen_path_size; ++ix, ++size) {
int offset = 0;
int len = 0;
int maxlen = chosen_path[ix];
if (maxlen != 1) {
HashChainFindCopy(hash_chain, quality,
i, xsize, argb, maxlen, &offset, &len);
assert(len == maxlen);
refs->refs[size] = PixOrCopyCreateCopy(offset, len);
if (use_color_cache) {
for (k = 0; k < len; ++k) {
VP8LColorCacheInsert(&hashers, argb[i + k]);
}
}
{
const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
for (k = 0; k < last; ++k) {
HashChainInsert(hash_chain, &argb[i + k], i + k);
}
}
i += len;
} else {
if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
// push pixel as a color cache index
const int idx = VP8LColorCacheGetIndex(&hashers, argb[i]);
refs->refs[size] = PixOrCopyCreateCacheIdx(idx);
} else {
refs->refs[size] = PixOrCopyCreateLiteral(argb[i]);
}
if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
if (i + 1 < pix_count) {
HashChainInsert(hash_chain, &argb[i], i);
}
++i;
}
}
assert(size <= refs->max_size);
refs->size = size;
ok = 1;
Error:
if (cc_init) VP8LColorCacheClear(&hashers);
HashChainDelete(hash_chain);
return ok;
}
// Returns 1 on success.
static int BackwardReferencesTraceBackwards(int xsize, int ysize,
int recursive_cost_model,
const uint32_t* const argb,
int cache_bits,
VP8LBackwardRefs* const refs) {
int ok = 0;
const int dist_array_size = xsize * ysize;
uint32_t* chosen_path = NULL;
int chosen_path_size = 0;
uint32_t* dist_array =
(uint32_t*)WebPSafeMalloc((uint64_t)dist_array_size, sizeof(*dist_array));
if (dist_array == NULL) goto Error;
if (!BackwardReferencesHashChainDistanceOnly(
xsize, ysize, recursive_cost_model, argb, cache_bits, dist_array)) {
goto Error;
}
if (!TraceBackwards(dist_array, dist_array_size,
&chosen_path, &chosen_path_size)) {
goto Error;
}
free(dist_array); // no need to retain this memory any longer
dist_array = NULL;
if (!BackwardReferencesHashChainFollowChosenPath(
xsize, ysize, argb, cache_bits, chosen_path, chosen_path_size, refs)) {
goto Error;
}
ok = 1;
Error:
free(chosen_path);
free(dist_array);
return ok;
}
static void BackwardReferences2DLocality(int xsize,
VP8LBackwardRefs* const refs) {
int i;
for (i = 0; i < refs->size; ++i) {
if (PixOrCopyIsCopy(&refs->refs[i])) {
const int dist = refs->refs[i].argb_or_distance;
const int transformed_dist = DistanceToPlaneCode(xsize, dist);
refs->refs[i].argb_or_distance = transformed_dist;
}
}
}
int VP8LGetBackwardReferences(int width, int height,
const uint32_t* const argb,
int quality, int cache_bits, int use_2d_locality,
VP8LBackwardRefs* const best) {
int ok = 0;
int lz77_is_useful;
VP8LBackwardRefs refs_rle, refs_lz77;
const int num_pix = width * height;
VP8LBackwardRefsAlloc(&refs_rle, num_pix);
VP8LBackwardRefsAlloc(&refs_lz77, num_pix);
VP8LInitBackwardRefs(best);
if (refs_rle.refs == NULL || refs_lz77.refs == NULL) {
Error1:
VP8LClearBackwardRefs(&refs_rle);
VP8LClearBackwardRefs(&refs_lz77);
goto End;
}
if (!BackwardReferencesHashChain(width, height, argb, cache_bits, quality,
&refs_lz77)) {
goto End;
}
// Backward Reference using RLE only.
BackwardReferencesRle(width, height, argb, &refs_rle);
{
double bit_cost_lz77, bit_cost_rle;
VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo));
if (histo == NULL) goto Error1;
// Evaluate lz77 coding
VP8LHistogramCreate(histo, &refs_lz77, cache_bits);
bit_cost_lz77 = VP8LHistogramEstimateBits(histo);
// Evaluate RLE coding
VP8LHistogramCreate(histo, &refs_rle, cache_bits);
bit_cost_rle = VP8LHistogramEstimateBits(histo);
// Decide if LZ77 is useful.
lz77_is_useful = (bit_cost_lz77 < bit_cost_rle);
free(histo);
}
// Choose appropriate backward reference.
if (lz77_is_useful) {
// TraceBackwards is costly. Run it for higher qualities.
const int try_lz77_trace_backwards = (quality >= 75);
*best = refs_lz77; // default guess: lz77 is better
VP8LClearBackwardRefs(&refs_rle);
if (try_lz77_trace_backwards) {
const int recursion_level = (num_pix < 320 * 200) ? 1 : 0;
VP8LBackwardRefs refs_trace;
if (!VP8LBackwardRefsAlloc(&refs_trace, num_pix)) {
goto End;
}
if (BackwardReferencesTraceBackwards(
width, height, recursion_level, argb, cache_bits, &refs_trace)) {
VP8LClearBackwardRefs(&refs_lz77);
*best = refs_trace;
}
}
} else {
VP8LClearBackwardRefs(&refs_lz77);
*best = refs_rle;
}
if (use_2d_locality) BackwardReferences2DLocality(width, best);
ok = 1;
End:
if (!ok) {
VP8LClearBackwardRefs(best);
}
return ok;
}
// Returns 1 on success.
static int ComputeCacheHistogram(const uint32_t* const argb,
int xsize, int ysize,
const VP8LBackwardRefs* const refs,
int cache_bits,
VP8LHistogram* const histo) {
int pixel_index = 0;
int i;
uint32_t k;
VP8LColorCache hashers;
const int use_color_cache = (cache_bits > 0);
int cc_init = 0;
if (use_color_cache) {
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
if (!cc_init) return 0;
}
for (i = 0; i < refs->size; ++i) {
const PixOrCopy* const v = &refs->refs[i];
if (PixOrCopyIsLiteral(v)) {
if (use_color_cache &&
VP8LColorCacheContains(&hashers, argb[pixel_index])) {
// push pixel as a cache index
const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]);
const PixOrCopy token = PixOrCopyCreateCacheIdx(ix);
VP8LHistogramAddSinglePixOrCopy(histo, &token);
} else {
VP8LHistogramAddSinglePixOrCopy(histo, v);
}
} else {
VP8LHistogramAddSinglePixOrCopy(histo, v);
}
if (use_color_cache) {
for (k = 0; k < PixOrCopyLength(v); ++k) {
VP8LColorCacheInsert(&hashers, argb[pixel_index + k]);
}
}
pixel_index += PixOrCopyLength(v);
}
assert(pixel_index == xsize * ysize);
(void)xsize; // xsize is not used in non-debug compilations otherwise.
(void)ysize; // ysize is not used in non-debug compilations otherwise.
if (cc_init) VP8LColorCacheClear(&hashers);
return 1;
}
// Returns how many bits are to be used for a color cache.
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
int xsize, int ysize,
int* const best_cache_bits) {
int ok = 0;
int cache_bits;
double lowest_entropy = 1e99;
VP8LBackwardRefs refs;
static const double kSmallPenaltyForLargeCache = 4.0;
static const int quality = 30;
if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize) ||
!BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, &refs)) {
goto Error;
}
for (cache_bits = 0; cache_bits <= MAX_COLOR_CACHE_BITS; ++cache_bits) {
double cur_entropy;
VP8LHistogram histo;
VP8LHistogramInit(&histo, cache_bits);
ComputeCacheHistogram(argb, xsize, ysize, &refs, cache_bits, &histo);
cur_entropy = VP8LHistogramEstimateBits(&histo) +
kSmallPenaltyForLargeCache * cache_bits;
if (cache_bits == 0 || cur_entropy < lowest_entropy) {
*best_cache_bits = cache_bits;
lowest_entropy = cur_entropy;
}
}
ok = 1;
Error:
VP8LClearBackwardRefs(&refs);
return ok;
}

View File

@ -0,0 +1,212 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Author: Jyrki Alakuijala (jyrki@google.com)
//
#ifndef WEBP_ENC_BACKWARD_REFERENCES_H_
#define WEBP_ENC_BACKWARD_REFERENCES_H_
#include <assert.h>
#include <stdlib.h>
#include "../webp/types.h"
#include "../webp/format_constants.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
// The spec allows 11, we use 9 bits to reduce memory consumption in encoding.
// Having 9 instead of 11 only removes about 0.25 % of compression density.
#define MAX_COLOR_CACHE_BITS 9
// Max ever number of codes we'll use:
#define PIX_OR_COPY_CODES_MAX \
(NUM_LITERAL_CODES + NUM_LENGTH_CODES + (1 << MAX_COLOR_CACHE_BITS))
// -----------------------------------------------------------------------------
// PrefixEncode()
// use GNU builtins where available.
#if defined(__GNUC__) && \
((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4)
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
return n == 0 ? -1 : 31 ^ __builtin_clz(n);
}
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
#include <intrin.h>
#pragma intrinsic(_BitScanReverse)
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
unsigned long first_set_bit;
return _BitScanReverse(&first_set_bit, n) ? first_set_bit : -1;
}
#else
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
int log = 0;
uint32_t value = n;
int i;
if (value == 0) return -1;
for (i = 4; i >= 0; --i) {
const int shift = (1 << i);
const uint32_t x = value >> shift;
if (x != 0) {
value = x;
log += shift;
}
}
return log;
}
#endif
static WEBP_INLINE int VP8LBitsLog2Ceiling(uint32_t n) {
const int floor = BitsLog2Floor(n);
if (n == (n & ~(n - 1))) // zero or a power of two.
return floor;
else
return floor + 1;
}
// Splitting of distance and length codes into prefixes and
// extra bits. The prefixes are encoded with an entropy code
// while the extra bits are stored just as normal bits.
static WEBP_INLINE void PrefixEncode(int distance, int* const code,
int* const extra_bits_count,
int* const extra_bits_value) {
// Collect the two most significant bits where the highest bit is 1.
const int highest_bit = BitsLog2Floor(--distance);
// & 0x3f is to make behavior well defined when highest_bit
// does not exist or is the least significant bit.
const int second_highest_bit =
(distance >> ((highest_bit - 1) & 0x3f)) & 1;
*extra_bits_count = (highest_bit > 0) ? (highest_bit - 1) : 0;
*extra_bits_value = distance & ((1 << *extra_bits_count) - 1);
*code = (highest_bit > 0) ? (2 * highest_bit + second_highest_bit)
: (highest_bit == 0) ? 1 : 0;
}
// -----------------------------------------------------------------------------
// PixOrCopy
enum Mode {
kLiteral,
kCacheIdx,
kCopy,
kNone
};
typedef struct {
// mode as uint8_t to make the memory layout to be exactly 8 bytes.
uint8_t mode;
uint16_t len;
uint32_t argb_or_distance;
} PixOrCopy;
static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t distance,
uint16_t len) {
PixOrCopy retval;
retval.mode = kCopy;
retval.argb_or_distance = distance;
retval.len = len;
return retval;
}
static WEBP_INLINE PixOrCopy PixOrCopyCreateCacheIdx(int idx) {
PixOrCopy retval;
assert(idx >= 0);
assert(idx < (1 << MAX_COLOR_CACHE_BITS));
retval.mode = kCacheIdx;
retval.argb_or_distance = idx;
retval.len = 1;
return retval;
}
static WEBP_INLINE PixOrCopy PixOrCopyCreateLiteral(uint32_t argb) {
PixOrCopy retval;
retval.mode = kLiteral;
retval.argb_or_distance = argb;
retval.len = 1;
return retval;
}
static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy* const p) {
return (p->mode == kLiteral);
}
static WEBP_INLINE int PixOrCopyIsCacheIdx(const PixOrCopy* const p) {
return (p->mode == kCacheIdx);
}
static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy* const p) {
return (p->mode == kCopy);
}
static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy* const p,
int component) {
assert(p->mode == kLiteral);
return (p->argb_or_distance >> (component * 8)) & 0xff;
}
static WEBP_INLINE uint32_t PixOrCopyLength(const PixOrCopy* const p) {
return p->len;
}
static WEBP_INLINE uint32_t PixOrCopyArgb(const PixOrCopy* const p) {
assert(p->mode == kLiteral);
return p->argb_or_distance;
}
static WEBP_INLINE uint32_t PixOrCopyCacheIdx(const PixOrCopy* const p) {
assert(p->mode == kCacheIdx);
assert(p->argb_or_distance < (1U << MAX_COLOR_CACHE_BITS));
return p->argb_or_distance;
}
static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) {
assert(p->mode == kCopy);
return p->argb_or_distance;
}
// -----------------------------------------------------------------------------
// VP8LBackwardRefs
typedef struct {
PixOrCopy* refs;
int size; // currently used
int max_size; // maximum capacity
} VP8LBackwardRefs;
// Initialize the object. Must be called first. 'refs' can be NULL.
void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs);
// Release memory and re-initialize the object. 'refs' can be NULL.
void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);
// Allocate 'max_size' references. Returns false in case of memory error.
int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size);
// -----------------------------------------------------------------------------
// Main entry points
// Evaluates best possible backward references for specified quality.
// Further optimize for 2D locality if use_2d_locality flag is set.
int VP8LGetBackwardReferences(int width, int height,
const uint32_t* const argb,
int quality, int cache_bits, int use_2d_locality,
VP8LBackwardRefs* const best);
// Produce an estimate for a good color cache size for the image.
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
int xsize, int ysize,
int* const best_cache_bits);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif // WEBP_ENC_BACKWARD_REFERENCES_H_

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,7 +9,6 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
#include "../webp/encode.h" #include "../webp/encode.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
@ -20,9 +19,9 @@ extern "C" {
// WebPConfig // WebPConfig
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int WebPConfigInitInternal(WebPConfig* const config, int WebPConfigInitInternal(WebPConfig* config,
WebPPreset preset, float quality, int version) { WebPPreset preset, float quality, int version) {
if (version != WEBP_ENCODER_ABI_VERSION) { if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
return 0; // caller/system version mismatch! return 0; // caller/system version mismatch!
} }
if (config == NULL) return 0; if (config == NULL) return 0;
@ -41,8 +40,12 @@ int WebPConfigInitInternal(WebPConfig* const config,
config->show_compressed = 0; config->show_compressed = 0;
config->preprocessing = 0; config->preprocessing = 0;
config->autofilter = 0; config->autofilter = 0;
config->alpha_compression = 0;
config->partition_limit = 0; config->partition_limit = 0;
config->alpha_compression = 1;
config->alpha_filtering = 1;
config->alpha_quality = 100;
config->lossless = 0;
config->image_hint = WEBP_HINT_DEFAULT;
// TODO(skal): tune. // TODO(skal): tune.
switch (preset) { switch (preset) {
@ -77,7 +80,7 @@ int WebPConfigInitInternal(WebPConfig* const config,
return WebPValidateConfig(config); return WebPValidateConfig(config);
} }
int WebPValidateConfig(const WebPConfig* const config) { int WebPValidateConfig(const WebPConfig* config) {
if (config == NULL) return 0; if (config == NULL) return 0;
if (config->quality < 0 || config->quality > 100) if (config->quality < 0 || config->quality > 100)
return 0; return 0;
@ -111,6 +114,14 @@ int WebPValidateConfig(const WebPConfig* const config) {
return 0; return 0;
if (config->alpha_compression < 0) if (config->alpha_compression < 0)
return 0; return 0;
if (config->alpha_filtering < 0)
return 0;
if (config->alpha_quality < 0 || config->alpha_quality > 100)
return 0;
if (config->lossless < 0 || config->lossless > 1)
return 0;
if (config->image_hint >= WEBP_HINT_LAST)
return 0;
return 1; return 1;
} }

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,9 +9,7 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <assert.h> #include "./cost.h"
#include "cost.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -52,9 +50,9 @@ const uint16_t VP8EntropyCost[256] = {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Level cost tables // Level cost tables
// For each given level, the following table given the pattern of contexts // For each given level, the following table gives the pattern of contexts to
// to use for coding it (in [][0]) as well as the bit value to use for // use for coding it (in [][0]) as well as the bit value to use for each
// each context (in [][1]). // context (in [][1]).
const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2] = { const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2] = {
{0x001, 0x000}, {0x007, 0x001}, {0x00f, 0x005}, {0x001, 0x000}, {0x007, 0x001}, {0x00f, 0x005},
{0x00f, 0x00d}, {0x033, 0x003}, {0x033, 0x003}, {0x033, 0x023}, {0x00f, 0x00d}, {0x033, 0x003}, {0x033, 0x003}, {0x033, 0x023},
@ -356,6 +354,9 @@ static int VariableLevelCost(int level, const uint8_t probas[NUM_PROBAS]) {
void VP8CalculateLevelCosts(VP8Proba* const proba) { void VP8CalculateLevelCosts(VP8Proba* const proba) {
int ctype, band, ctx; int ctype, band, ctx;
if (!proba->dirty_) return; // nothing to do.
for (ctype = 0; ctype < NUM_TYPES; ++ctype) { for (ctype = 0; ctype < NUM_TYPES; ++ctype) {
for (band = 0; band < NUM_BANDS; ++band) { for (band = 0; band < NUM_BANDS; ++band) {
for(ctx = 0; ctx < NUM_CTX; ++ctx) { for(ctx = 0; ctx < NUM_CTX; ++ctx) {
@ -372,6 +373,7 @@ void VP8CalculateLevelCosts(VP8Proba* const proba) {
} }
} }
} }
proba->dirty_ = 0;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -12,7 +12,7 @@
#ifndef WEBP_ENC_COST_H_ #ifndef WEBP_ENC_COST_H_
#define WEBP_ENC_COST_H_ #define WEBP_ENC_COST_H_
#include "vp8enci.h" #include "./vp8enci.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -22,22 +22,16 @@ extern const uint16_t VP8LevelFixedCosts[2048]; // approximate cost per level
extern const uint16_t VP8EntropyCost[256]; // 8bit fixed-point log(p) extern const uint16_t VP8EntropyCost[256]; // 8bit fixed-point log(p)
// Cost of coding one event with probability 'proba'. // Cost of coding one event with probability 'proba'.
static inline int VP8BitCost(int bit, uint8_t proba) { static WEBP_INLINE int VP8BitCost(int bit, uint8_t proba) {
return !bit ? VP8EntropyCost[proba] : VP8EntropyCost[255 - proba]; return !bit ? VP8EntropyCost[proba] : VP8EntropyCost[255 - proba];
} }
// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability.
static inline uint64_t VP8BranchCost(uint64_t nb, uint64_t total,
uint8_t proba) {
return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba);
}
// Level cost calculations // Level cost calculations
extern const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2]; extern const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2];
void VP8CalculateLevelCosts(VP8Proba* const proba); void VP8CalculateLevelCosts(VP8Proba* const proba);
static inline int VP8LevelCost(const uint16_t* const table, int level) { static WEBP_INLINE int VP8LevelCost(const uint16_t* const table, int level) {
return VP8LevelFixedCosts[level] return VP8LevelFixedCosts[level]
+ table[level > MAX_VARIABLE_LEVEL ? MAX_VARIABLE_LEVEL : level]; + table[(level > MAX_VARIABLE_LEVEL) ? MAX_VARIABLE_LEVEL : level];
} }
// Mode costs // Mode costs

View File

@ -1,770 +0,0 @@
// Copyright 2011 Google Inc.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// speed-critical functions.
//
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
#include "vp8enci.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------
// Compute susceptibility based on DCT-coeff histograms:
// the higher, the "easier" the macroblock is to compress.
static int ClipAlpha(int alpha) {
return alpha < 0 ? 0 : alpha > 255 ? 255 : alpha;
}
int VP8GetAlpha(const int histo[MAX_COEFF_THRESH + 1]) {
int num = 0, den = 0, val = 0;
int k;
int alpha;
// note: changing this loop to avoid the numerous "k + 1" slows things down.
for (k = 0; k < MAX_COEFF_THRESH; ++k) {
if (histo[k + 1]) {
val += histo[k + 1];
num += val * (k + 1);
den += (k + 1) * (k + 1);
}
}
// we scale the value to a usable [0..255] range
alpha = den ? 10 * num / den - 5 : 0;
return ClipAlpha(alpha);
}
static int CollectHistogram(const uint8_t* ref, const uint8_t* pred,
int start_block, int end_block) {
int histo[MAX_COEFF_THRESH + 1] = { 0 };
int16_t out[16];
int j, k;
for (j = start_block; j < end_block; ++j) {
VP8FTransform(ref + VP8Scan[j], pred + VP8Scan[j], out);
// Convert coefficients to bin (within out[]).
for (k = 0; k < 16; ++k) {
const int v = abs(out[k]) >> 2;
out[k] = (v > MAX_COEFF_THRESH) ? MAX_COEFF_THRESH : v;
}
// Use bin to update histogram.
for (k = 0; k < 16; ++k) {
histo[out[k]]++;
}
}
return VP8GetAlpha(histo);
}
//------------------------------------------------------------------------------
// run-time tables (~4k)
static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
// We declare this variable 'volatile' to prevent instruction reordering
// and make sure it's set to true _last_ (so as to be thread-safe)
static volatile int tables_ok = 0;
static void InitTables(void) {
if (!tables_ok) {
int i;
for (i = -255; i <= 255 + 255; ++i) {
clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
}
tables_ok = 1;
}
}
static inline uint8_t clip_8b(int v) {
return (!(v & ~0xff)) ? v : v < 0 ? 0 : 255;
}
//------------------------------------------------------------------------------
// Transforms (Paragraph 14.4)
#define STORE(x, y, v) \
dst[(x) + (y) * BPS] = clip_8b(ref[(x) + (y) * BPS] + ((v) >> 3))
static const int kC1 = 20091 + (1 << 16);
static const int kC2 = 35468;
#define MUL(a, b) (((a) * (b)) >> 16)
static inline void ITransformOne(const uint8_t* ref, const int16_t* in,
uint8_t* dst) {
int C[4 * 4], *tmp;
int i;
tmp = C;
for (i = 0; i < 4; ++i) { // vertical pass
const int a = in[0] + in[8];
const int b = in[0] - in[8];
const int c = MUL(in[4], kC2) - MUL(in[12], kC1);
const int d = MUL(in[4], kC1) + MUL(in[12], kC2);
tmp[0] = a + d;
tmp[1] = b + c;
tmp[2] = b - c;
tmp[3] = a - d;
tmp += 4;
in++;
}
tmp = C;
for (i = 0; i < 4; ++i) { // horizontal pass
const int dc = tmp[0] + 4;
const int a = dc + tmp[8];
const int b = dc - tmp[8];
const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1);
const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2);
STORE(0, i, a + d);
STORE(1, i, b + c);
STORE(2, i, b - c);
STORE(3, i, a - d);
tmp++;
}
}
static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst,
int do_two) {
ITransformOne(ref, in, dst);
if (do_two) {
ITransformOne(ref + 4, in + 16, dst + 4);
}
}
static void FTransform(const uint8_t* src, const uint8_t* ref, int16_t* out) {
int i;
int tmp[16];
for (i = 0; i < 4; ++i, src += BPS, ref += BPS) {
const int d0 = src[0] - ref[0];
const int d1 = src[1] - ref[1];
const int d2 = src[2] - ref[2];
const int d3 = src[3] - ref[3];
const int a0 = (d0 + d3) << 3;
const int a1 = (d1 + d2) << 3;
const int a2 = (d1 - d2) << 3;
const int a3 = (d0 - d3) << 3;
tmp[0 + i * 4] = (a0 + a1);
tmp[1 + i * 4] = (a2 * 2217 + a3 * 5352 + 14500) >> 12;
tmp[2 + i * 4] = (a0 - a1);
tmp[3 + i * 4] = (a3 * 2217 - a2 * 5352 + 7500) >> 12;
}
for (i = 0; i < 4; ++i) {
const int a0 = (tmp[0 + i] + tmp[12 + i]);
const int a1 = (tmp[4 + i] + tmp[ 8 + i]);
const int a2 = (tmp[4 + i] - tmp[ 8 + i]);
const int a3 = (tmp[0 + i] - tmp[12 + i]);
out[0 + i] = (a0 + a1 + 7) >> 4;
out[4 + i] = ((a2 * 2217 + a3 * 5352 + 12000) >> 16) + (a3 != 0);
out[8 + i] = (a0 - a1 + 7) >> 4;
out[12+ i] = ((a3 * 2217 - a2 * 5352 + 51000) >> 16);
}
}
static void ITransformWHT(const int16_t* in, int16_t* out) {
int tmp[16];
int i;
for (i = 0; i < 4; ++i) {
const int a0 = in[0 + i] + in[12 + i];
const int a1 = in[4 + i] + in[ 8 + i];
const int a2 = in[4 + i] - in[ 8 + i];
const int a3 = in[0 + i] - in[12 + i];
tmp[0 + i] = a0 + a1;
tmp[8 + i] = a0 - a1;
tmp[4 + i] = a3 + a2;
tmp[12 + i] = a3 - a2;
}
for (i = 0; i < 4; ++i) {
const int dc = tmp[0 + i * 4] + 3; // w/ rounder
const int a0 = dc + tmp[3 + i * 4];
const int a1 = tmp[1 + i * 4] + tmp[2 + i * 4];
const int a2 = tmp[1 + i * 4] - tmp[2 + i * 4];
const int a3 = dc - tmp[3 + i * 4];
out[ 0] = (a0 + a1) >> 3;
out[16] = (a3 + a2) >> 3;
out[32] = (a0 - a1) >> 3;
out[48] = (a3 - a2) >> 3;
out += 64;
}
}
static void FTransformWHT(const int16_t* in, int16_t* out) {
int tmp[16];
int i;
for (i = 0; i < 4; ++i, in += 64) {
const int a0 = (in[0 * 16] + in[2 * 16]) << 2;
const int a1 = (in[1 * 16] + in[3 * 16]) << 2;
const int a2 = (in[1 * 16] - in[3 * 16]) << 2;
const int a3 = (in[0 * 16] - in[2 * 16]) << 2;
tmp[0 + i * 4] = (a0 + a1) + (a0 != 0);
tmp[1 + i * 4] = a3 + a2;
tmp[2 + i * 4] = a3 - a2;
tmp[3 + i * 4] = a0 - a1;
}
for (i = 0; i < 4; ++i) {
const int a0 = (tmp[0 + i] + tmp[8 + i]);
const int a1 = (tmp[4 + i] + tmp[12+ i]);
const int a2 = (tmp[4 + i] - tmp[12+ i]);
const int a3 = (tmp[0 + i] - tmp[8 + i]);
const int b0 = a0 + a1;
const int b1 = a3 + a2;
const int b2 = a3 - a2;
const int b3 = a0 - a1;
out[ 0 + i] = (b0 + (b0 > 0) + 3) >> 3;
out[ 4 + i] = (b1 + (b1 > 0) + 3) >> 3;
out[ 8 + i] = (b2 + (b2 > 0) + 3) >> 3;
out[12 + i] = (b3 + (b3 > 0) + 3) >> 3;
}
}
#undef MUL
#undef STORE
//------------------------------------------------------------------------------
// Intra predictions
#define OUT(x, y) dst[(x) + (y) * BPS]
static inline void Fill(uint8_t* dst, int value, int size) {
int j;
for (j = 0; j < size; ++j) {
memset(dst + j * BPS, value, size);
}
}
static inline void VerticalPred(uint8_t* dst, const uint8_t* top, int size) {
int j;
if (top) {
for (j = 0; j < size; ++j) memcpy(dst + j * BPS, top, size);
} else {
Fill(dst, 127, size);
}
}
static inline void HorizontalPred(uint8_t* dst, const uint8_t* left, int size) {
if (left) {
int j;
for (j = 0; j < size; ++j) {
memset(dst + j * BPS, left[j], size);
}
} else {
Fill(dst, 129, size);
}
}
static inline void TrueMotion(uint8_t* dst, const uint8_t* left,
const uint8_t* top, int size) {
int y;
if (left) {
if (top) {
const uint8_t* const clip = clip1 + 255 - left[-1];
for (y = 0; y < size; ++y) {
const uint8_t* const clip_table = clip + left[y];
int x;
for (x = 0; x < size; ++x) {
dst[x] = clip_table[top[x]];
}
dst += BPS;
}
} else {
HorizontalPred(dst, left, size);
}
} else {
// true motion without left samples (hence: with default 129 value)
// is equivalent to VE prediction where you just copy the top samples.
// Note that if top samples are not available, the default value is
// then 129, and not 127 as in the VerticalPred case.
if (top) {
VerticalPred(dst, top, size);
} else {
Fill(dst, 129, size);
}
}
}
static inline void DCMode(uint8_t* dst, const uint8_t* left,
const uint8_t* top,
int size, int round, int shift) {
int DC = 0;
int j;
if (top) {
for (j = 0; j < size; ++j) DC += top[j];
if (left) { // top and left present
for (j = 0; j < size; ++j) DC += left[j];
} else { // top, but no left
DC += DC;
}
DC = (DC + round) >> shift;
} else if (left) { // left but no top
for (j = 0; j < size; ++j) DC += left[j];
DC += DC;
DC = (DC + round) >> shift;
} else { // no top, no left, nothing.
DC = 0x80;
}
Fill(dst, DC, size);
}
//------------------------------------------------------------------------------
// Chroma 8x8 prediction (paragraph 12.2)
static void IntraChromaPreds(uint8_t* dst, const uint8_t* left,
const uint8_t* top) {
// U block
DCMode(C8DC8 + dst, left, top, 8, 8, 4);
VerticalPred(C8VE8 + dst, top, 8);
HorizontalPred(C8HE8 + dst, left, 8);
TrueMotion(C8TM8 + dst, left, top, 8);
// V block
dst += 8;
if (top) top += 8;
if (left) left += 16;
DCMode(C8DC8 + dst, left, top, 8, 8, 4);
VerticalPred(C8VE8 + dst, top, 8);
HorizontalPred(C8HE8 + dst, left, 8);
TrueMotion(C8TM8 + dst, left, top, 8);
}
//------------------------------------------------------------------------------
// luma 16x16 prediction (paragraph 12.3)
static void Intra16Preds(uint8_t* dst,
const uint8_t* left, const uint8_t* top) {
DCMode(I16DC16 + dst, left, top, 16, 16, 5);
VerticalPred(I16VE16 + dst, top, 16);
HorizontalPred(I16HE16 + dst, left, 16);
TrueMotion(I16TM16 + dst, left, top, 16);
}
//------------------------------------------------------------------------------
// luma 4x4 prediction
#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
#define AVG2(a, b) (((a) + (b) + 1) >> 1)
static void VE4(uint8_t* dst, const uint8_t* top) { // vertical
const uint8_t vals[4] = {
AVG3(top[-1], top[0], top[1]),
AVG3(top[ 0], top[1], top[2]),
AVG3(top[ 1], top[2], top[3]),
AVG3(top[ 2], top[3], top[4])
};
int i;
for (i = 0; i < 4; ++i) {
memcpy(dst + i * BPS, vals, 4);
}
}
static void HE4(uint8_t* dst, const uint8_t* top) { // horizontal
const int X = top[-1];
const int I = top[-2];
const int J = top[-3];
const int K = top[-4];
const int L = top[-5];
*(uint32_t*)(dst + 0 * BPS) = 0x01010101U * AVG3(X, I, J);
*(uint32_t*)(dst + 1 * BPS) = 0x01010101U * AVG3(I, J, K);
*(uint32_t*)(dst + 2 * BPS) = 0x01010101U * AVG3(J, K, L);
*(uint32_t*)(dst + 3 * BPS) = 0x01010101U * AVG3(K, L, L);
}
static void DC4(uint8_t* dst, const uint8_t* top) {
uint32_t dc = 4;
int i;
for (i = 0; i < 4; ++i) dc += top[i] + top[-5 + i];
Fill(dst, dc >> 3, 4);
}
static void RD4(uint8_t* dst, const uint8_t* top) {
const int X = top[-1];
const int I = top[-2];
const int J = top[-3];
const int K = top[-4];
const int L = top[-5];
const int A = top[0];
const int B = top[1];
const int C = top[2];
const int D = top[3];
OUT(0, 3) = AVG3(J, K, L);
OUT(0, 2) = OUT(1, 3) = AVG3(I, J, K);
OUT(0, 1) = OUT(1, 2) = OUT(2, 3) = AVG3(X, I, J);
OUT(0, 0) = OUT(1, 1) = OUT(2, 2) = OUT(3, 3) = AVG3(A, X, I);
OUT(1, 0) = OUT(2, 1) = OUT(3, 2) = AVG3(B, A, X);
OUT(2, 0) = OUT(3, 1) = AVG3(C, B, A);
OUT(3, 0) = AVG3(D, C, B);
}
static void LD4(uint8_t* dst, const uint8_t* top) {
const int A = top[0];
const int B = top[1];
const int C = top[2];
const int D = top[3];
const int E = top[4];
const int F = top[5];
const int G = top[6];
const int H = top[7];
OUT(0, 0) = AVG3(A, B, C);
OUT(1, 0) = OUT(0, 1) = AVG3(B, C, D);
OUT(2, 0) = OUT(1, 1) = OUT(0, 2) = AVG3(C, D, E);
OUT(3, 0) = OUT(2, 1) = OUT(1, 2) = OUT(0, 3) = AVG3(D, E, F);
OUT(3, 1) = OUT(2, 2) = OUT(1, 3) = AVG3(E, F, G);
OUT(3, 2) = OUT(2, 3) = AVG3(F, G, H);
OUT(3, 3) = AVG3(G, H, H);
}
static void VR4(uint8_t* dst, const uint8_t* top) {
const int X = top[-1];
const int I = top[-2];
const int J = top[-3];
const int K = top[-4];
const int A = top[0];
const int B = top[1];
const int C = top[2];
const int D = top[3];
OUT(0, 0) = OUT(1, 2) = AVG2(X, A);
OUT(1, 0) = OUT(2, 2) = AVG2(A, B);
OUT(2, 0) = OUT(3, 2) = AVG2(B, C);
OUT(3, 0) = AVG2(C, D);
OUT(0, 3) = AVG3(K, J, I);
OUT(0, 2) = AVG3(J, I, X);
OUT(0, 1) = OUT(1, 3) = AVG3(I, X, A);
OUT(1, 1) = OUT(2, 3) = AVG3(X, A, B);
OUT(2, 1) = OUT(3, 3) = AVG3(A, B, C);
OUT(3, 1) = AVG3(B, C, D);
}
static void VL4(uint8_t* dst, const uint8_t* top) {
const int A = top[0];
const int B = top[1];
const int C = top[2];
const int D = top[3];
const int E = top[4];
const int F = top[5];
const int G = top[6];
const int H = top[7];
OUT(0, 0) = AVG2(A, B);
OUT(1, 0) = OUT(0, 2) = AVG2(B, C);
OUT(2, 0) = OUT(1, 2) = AVG2(C, D);
OUT(3, 0) = OUT(2, 2) = AVG2(D, E);
OUT(0, 1) = AVG3(A, B, C);
OUT(1, 1) = OUT(0, 3) = AVG3(B, C, D);
OUT(2, 1) = OUT(1, 3) = AVG3(C, D, E);
OUT(3, 1) = OUT(2, 3) = AVG3(D, E, F);
OUT(3, 2) = AVG3(E, F, G);
OUT(3, 3) = AVG3(F, G, H);
}
static void HU4(uint8_t* dst, const uint8_t* top) {
const int I = top[-2];
const int J = top[-3];
const int K = top[-4];
const int L = top[-5];
OUT(0, 0) = AVG2(I, J);
OUT(2, 0) = OUT(0, 1) = AVG2(J, K);
OUT(2, 1) = OUT(0, 2) = AVG2(K, L);
OUT(1, 0) = AVG3(I, J, K);
OUT(3, 0) = OUT(1, 1) = AVG3(J, K, L);
OUT(3, 1) = OUT(1, 2) = AVG3(K, L, L);
OUT(3, 2) = OUT(2, 2) =
OUT(0, 3) = OUT(1, 3) = OUT(2, 3) = OUT(3, 3) = L;
}
static void HD4(uint8_t* dst, const uint8_t* top) {
const int X = top[-1];
const int I = top[-2];
const int J = top[-3];
const int K = top[-4];
const int L = top[-5];
const int A = top[0];
const int B = top[1];
const int C = top[2];
OUT(0, 0) = OUT(2, 1) = AVG2(I, X);
OUT(0, 1) = OUT(2, 2) = AVG2(J, I);
OUT(0, 2) = OUT(2, 3) = AVG2(K, J);
OUT(0, 3) = AVG2(L, K);
OUT(3, 0) = AVG3(A, B, C);
OUT(2, 0) = AVG3(X, A, B);
OUT(1, 0) = OUT(3, 1) = AVG3(I, X, A);
OUT(1, 1) = OUT(3, 2) = AVG3(J, I, X);
OUT(1, 2) = OUT(3, 3) = AVG3(K, J, I);
OUT(1, 3) = AVG3(L, K, J);
}
static void TM4(uint8_t* dst, const uint8_t* top) {
int x, y;
const uint8_t* const clip = clip1 + 255 - top[-1];
for (y = 0; y < 4; ++y) {
const uint8_t* const clip_table = clip + top[-2 - y];
for (x = 0; x < 4; ++x) {
dst[x] = clip_table[top[x]];
}
dst += BPS;
}
}
#undef AVG3
#undef AVG2
// Left samples are top[-5 .. -2], top_left is top[-1], top are
// located at top[0..3], and top right is top[4..7]
static void Intra4Preds(uint8_t* dst, const uint8_t* top) {
DC4(I4DC4 + dst, top);
TM4(I4TM4 + dst, top);
VE4(I4VE4 + dst, top);
HE4(I4HE4 + dst, top);
RD4(I4RD4 + dst, top);
VR4(I4VR4 + dst, top);
LD4(I4LD4 + dst, top);
VL4(I4VL4 + dst, top);
HD4(I4HD4 + dst, top);
HU4(I4HU4 + dst, top);
}
//------------------------------------------------------------------------------
// Metric
static inline int GetSSE(const uint8_t* a, const uint8_t* b, int w, int h) {
int count = 0;
int y, x;
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
const int diff = (int)a[x] - b[x];
count += diff * diff;
}
a += BPS;
b += BPS;
}
return count;
}
static int SSE16x16(const uint8_t* a, const uint8_t* b) {
return GetSSE(a, b, 16, 16);
}
static int SSE16x8(const uint8_t* a, const uint8_t* b) {
return GetSSE(a, b, 16, 8);
}
static int SSE8x8(const uint8_t* a, const uint8_t* b) {
return GetSSE(a, b, 8, 8);
}
static int SSE4x4(const uint8_t* a, const uint8_t* b) {
return GetSSE(a, b, 4, 4);
}
//------------------------------------------------------------------------------
// Texture distortion
//
// We try to match the spectral content (weighted) between source and
// reconstructed samples.
// Hadamard transform
// Returns the weighted sum of the absolute value of transformed coefficients.
static int TTransform(const uint8_t* in, const uint16_t* w) {
int sum = 0;
int tmp[16];
int i;
// horizontal pass
for (i = 0; i < 4; ++i, in += BPS) {
const int a0 = (in[0] + in[2]) << 2;
const int a1 = (in[1] + in[3]) << 2;
const int a2 = (in[1] - in[3]) << 2;
const int a3 = (in[0] - in[2]) << 2;
tmp[0 + i * 4] = a0 + a1 + (a0 != 0);
tmp[1 + i * 4] = a3 + a2;
tmp[2 + i * 4] = a3 - a2;
tmp[3 + i * 4] = a0 - a1;
}
// vertical pass
for (i = 0; i < 4; ++i, ++w) {
const int a0 = (tmp[0 + i] + tmp[8 + i]);
const int a1 = (tmp[4 + i] + tmp[12+ i]);
const int a2 = (tmp[4 + i] - tmp[12+ i]);
const int a3 = (tmp[0 + i] - tmp[8 + i]);
const int b0 = a0 + a1;
const int b1 = a3 + a2;
const int b2 = a3 - a2;
const int b3 = a0 - a1;
// abs((b + (b<0) + 3) >> 3) = (abs(b) + 3) >> 3
sum += w[ 0] * ((abs(b0) + 3) >> 3);
sum += w[ 4] * ((abs(b1) + 3) >> 3);
sum += w[ 8] * ((abs(b2) + 3) >> 3);
sum += w[12] * ((abs(b3) + 3) >> 3);
}
return sum;
}
static int Disto4x4(const uint8_t* const a, const uint8_t* const b,
const uint16_t* const w) {
const int sum1 = TTransform(a, w);
const int sum2 = TTransform(b, w);
return (abs(sum2 - sum1) + 8) >> 4;
}
static int Disto16x16(const uint8_t* const a, const uint8_t* const b,
const uint16_t* const w) {
int D = 0;
int x, y;
for (y = 0; y < 16 * BPS; y += 4 * BPS) {
for (x = 0; x < 16; x += 4) {
D += Disto4x4(a + x + y, b + x + y, w);
}
}
return D;
}
//------------------------------------------------------------------------------
// Quantization
//
// Simple quantization
static int QuantizeBlock(int16_t in[16], int16_t out[16],
int n, const VP8Matrix* const mtx) {
int last = -1;
for (; n < 16; ++n) {
const int j = VP8Zigzag[n];
const int sign = (in[j] < 0);
int coeff = (sign ? -in[j] : in[j]) + mtx->sharpen_[j];
if (coeff > 2047) coeff = 2047;
if (coeff > mtx->zthresh_[j]) {
const int Q = mtx->q_[j];
const int iQ = mtx->iq_[j];
const int B = mtx->bias_[j];
out[n] = QUANTDIV(coeff, iQ, B);
if (sign) out[n] = -out[n];
in[j] = out[n] * Q;
if (out[n]) last = n;
} else {
out[n] = 0;
in[j] = 0;
}
}
return (last >= 0);
}
//------------------------------------------------------------------------------
// Block copy
static inline void Copy(const uint8_t* src, uint8_t* dst, int size) {
int y;
for (y = 0; y < size; ++y) {
memcpy(dst, src, size);
src += BPS;
dst += BPS;
}
}
static void Copy4x4(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 4); }
static void Copy8x8(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 8); }
static void Copy16x16(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 16); }
//------------------------------------------------------------------------------
// SSE2 detection.
//
#if defined(__pic__) && defined(__i386__)
static inline void GetCPUInfo(int cpu_info[4], int info_type) {
__asm__ volatile (
"mov %%ebx, %%edi\n"
"cpuid\n"
"xchg %%edi, %%ebx\n"
: "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
: "a"(info_type));
}
#elif defined(__i386__) || defined(__x86_64__)
static inline void GetCPUInfo(int cpu_info[4], int info_type) {
__asm__ volatile (
"cpuid\n"
: "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
: "a"(info_type));
}
#elif defined(_MSC_VER) // Visual C++
#define GetCPUInfo __cpuid
#endif
#if defined(__i386__) || defined(__x86_64__) || defined(_MSC_VER)
static int x86CPUInfo(CPUFeature feature) {
int cpu_info[4];
GetCPUInfo(cpu_info, 1);
if (feature == kSSE2) {
return 0 != (cpu_info[3] & 0x04000000);
}
if (feature == kSSE3) {
return 0 != (cpu_info[2] & 0x00000001);
}
return 0;
}
VP8CPUInfo VP8EncGetCPUInfo = x86CPUInfo;
#else
VP8CPUInfo VP8EncGetCPUInfo = NULL;
#endif
// Speed-critical function pointers. We have to initialize them to the default
// implementations within VP8EncDspInit().
VP8CHisto VP8CollectHistogram;
VP8Idct VP8ITransform;
VP8Fdct VP8FTransform;
VP8WHT VP8ITransformWHT;
VP8WHT VP8FTransformWHT;
VP8Intra4Preds VP8EncPredLuma4;
VP8IntraPreds VP8EncPredLuma16;
VP8IntraPreds VP8EncPredChroma8;
VP8Metric VP8SSE16x16;
VP8Metric VP8SSE8x8;
VP8Metric VP8SSE16x8;
VP8Metric VP8SSE4x4;
VP8WMetric VP8TDisto4x4;
VP8WMetric VP8TDisto16x16;
VP8QuantizeBlock VP8EncQuantizeBlock;
VP8BlockCopy VP8Copy4x4;
VP8BlockCopy VP8Copy8x8;
VP8BlockCopy VP8Copy16x16;
extern void VP8EncDspInitSSE2(void);
void VP8EncDspInit(void) {
InitTables();
// default C implementations
VP8CollectHistogram = CollectHistogram;
VP8ITransform = ITransform;
VP8FTransform = FTransform;
VP8ITransformWHT = ITransformWHT;
VP8FTransformWHT = FTransformWHT;
VP8EncPredLuma4 = Intra4Preds;
VP8EncPredLuma16 = Intra16Preds;
VP8EncPredChroma8 = IntraChromaPreds;
VP8SSE16x16 = SSE16x16;
VP8SSE8x8 = SSE8x8;
VP8SSE16x8 = SSE16x8;
VP8SSE4x4 = SSE4x4;
VP8TDisto4x4 = Disto4x4;
VP8TDisto16x16 = Disto16x16;
VP8EncQuantizeBlock = QuantizeBlock;
VP8Copy4x4 = Copy4x4;
VP8Copy8x8 = Copy8x8;
VP8Copy16x16 = Copy16x16;
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8EncGetCPUInfo) {
if (VP8EncGetCPUInfo(kSSE2)) {
#if defined(__SSE2__) || defined(_MSC_VER)
VP8EncDspInitSSE2();
#endif
}
if (VP8EncGetCPUInfo(kSSE3)) {
// later we'll plug some SSE3 variant here
}
}
}
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -1,834 +0,0 @@
// Copyright 2011 Google Inc.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// SSE2 version of speed-critical functions.
//
// Author: Christian Duvivier (cduvivier@google.com)
#if defined(__SSE2__) || defined(_MSC_VER)
#include <emmintrin.h>
#include "vp8enci.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------
// Compute susceptibility based on DCT-coeff histograms:
// the higher, the "easier" the macroblock is to compress.
static int CollectHistogramSSE2(const uint8_t* ref, const uint8_t* pred,
int start_block, int end_block) {
int histo[MAX_COEFF_THRESH + 1] = { 0 };
int16_t out[16];
int j, k;
const __m128i max_coeff_thresh = _mm_set1_epi16(MAX_COEFF_THRESH);
for (j = start_block; j < end_block; ++j) {
VP8FTransform(ref + VP8Scan[j], pred + VP8Scan[j], out);
// Convert coefficients to bin (within out[]).
{
// Load.
const __m128i out0 = _mm_loadu_si128((__m128i*)&out[0]);
const __m128i out1 = _mm_loadu_si128((__m128i*)&out[8]);
// sign(out) = out >> 15 (0x0000 if positive, 0xffff if negative)
const __m128i sign0 = _mm_srai_epi16(out0, 15);
const __m128i sign1 = _mm_srai_epi16(out1, 15);
// abs(out) = (out ^ sign) - sign
const __m128i xor0 = _mm_xor_si128(out0, sign0);
const __m128i xor1 = _mm_xor_si128(out1, sign1);
const __m128i abs0 = _mm_sub_epi16(xor0, sign0);
const __m128i abs1 = _mm_sub_epi16(xor1, sign1);
// v = abs(out) >> 2
const __m128i v0 = _mm_srai_epi16(abs0, 2);
const __m128i v1 = _mm_srai_epi16(abs1, 2);
// bin = min(v, MAX_COEFF_THRESH)
const __m128i bin0 = _mm_min_epi16(v0, max_coeff_thresh);
const __m128i bin1 = _mm_min_epi16(v1, max_coeff_thresh);
// Store.
_mm_storeu_si128((__m128i*)&out[0], bin0);
_mm_storeu_si128((__m128i*)&out[8], bin1);
}
// Use bin to update histogram.
for (k = 0; k < 16; ++k) {
histo[out[k]]++;
}
}
return VP8GetAlpha(histo);
}
//------------------------------------------------------------------------------
// Transforms (Paragraph 14.4)
// Does one or two inverse transforms.
static void ITransformSSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
int do_two) {
// This implementation makes use of 16-bit fixed point versions of two
// multiply constants:
// K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16
// K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16
//
// To be able to use signed 16-bit integers, we use the following trick to
// have constants within range:
// - Associated constants are obtained by subtracting the 16-bit fixed point
// version of one:
// k = K - (1 << 16) => K = k + (1 << 16)
// K1 = 85267 => k1 = 20091
// K2 = 35468 => k2 = -30068
// - The multiplication of a variable by a constant become the sum of the
// variable and the multiplication of that variable by the associated
// constant:
// (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x
const __m128i k1 = _mm_set1_epi16(20091);
const __m128i k2 = _mm_set1_epi16(-30068);
__m128i T0, T1, T2, T3;
// Load and concatenate the transform coefficients (we'll do two inverse
// transforms in parallel). In the case of only one inverse transform, the
// second half of the vectors will just contain random value we'll never
// use nor store.
__m128i in0, in1, in2, in3;
{
in0 = _mm_loadl_epi64((__m128i*)&in[0]);
in1 = _mm_loadl_epi64((__m128i*)&in[4]);
in2 = _mm_loadl_epi64((__m128i*)&in[8]);
in3 = _mm_loadl_epi64((__m128i*)&in[12]);
// a00 a10 a20 a30 x x x x
// a01 a11 a21 a31 x x x x
// a02 a12 a22 a32 x x x x
// a03 a13 a23 a33 x x x x
if (do_two) {
const __m128i inB0 = _mm_loadl_epi64((__m128i*)&in[16]);
const __m128i inB1 = _mm_loadl_epi64((__m128i*)&in[20]);
const __m128i inB2 = _mm_loadl_epi64((__m128i*)&in[24]);
const __m128i inB3 = _mm_loadl_epi64((__m128i*)&in[28]);
in0 = _mm_unpacklo_epi64(in0, inB0);
in1 = _mm_unpacklo_epi64(in1, inB1);
in2 = _mm_unpacklo_epi64(in2, inB2);
in3 = _mm_unpacklo_epi64(in3, inB3);
// a00 a10 a20 a30 b00 b10 b20 b30
// a01 a11 a21 a31 b01 b11 b21 b31
// a02 a12 a22 a32 b02 b12 b22 b32
// a03 a13 a23 a33 b03 b13 b23 b33
}
}
// Vertical pass and subsequent transpose.
{
// First pass, c and d calculations are longer because of the "trick"
// multiplications.
const __m128i a = _mm_add_epi16(in0, in2);
const __m128i b = _mm_sub_epi16(in0, in2);
// c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
const __m128i c1 = _mm_mulhi_epi16(in1, k2);
const __m128i c2 = _mm_mulhi_epi16(in3, k1);
const __m128i c3 = _mm_sub_epi16(in1, in3);
const __m128i c4 = _mm_sub_epi16(c1, c2);
const __m128i c = _mm_add_epi16(c3, c4);
// d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3
const __m128i d1 = _mm_mulhi_epi16(in1, k1);
const __m128i d2 = _mm_mulhi_epi16(in3, k2);
const __m128i d3 = _mm_add_epi16(in1, in3);
const __m128i d4 = _mm_add_epi16(d1, d2);
const __m128i d = _mm_add_epi16(d3, d4);
// Second pass.
const __m128i tmp0 = _mm_add_epi16(a, d);
const __m128i tmp1 = _mm_add_epi16(b, c);
const __m128i tmp2 = _mm_sub_epi16(b, c);
const __m128i tmp3 = _mm_sub_epi16(a, d);
// Transpose the two 4x4.
// a00 a01 a02 a03 b00 b01 b02 b03
// a10 a11 a12 a13 b10 b11 b12 b13
// a20 a21 a22 a23 b20 b21 b22 b23
// a30 a31 a32 a33 b30 b31 b32 b33
const __m128i transpose0_0 = _mm_unpacklo_epi16(tmp0, tmp1);
const __m128i transpose0_1 = _mm_unpacklo_epi16(tmp2, tmp3);
const __m128i transpose0_2 = _mm_unpackhi_epi16(tmp0, tmp1);
const __m128i transpose0_3 = _mm_unpackhi_epi16(tmp2, tmp3);
// a00 a10 a01 a11 a02 a12 a03 a13
// a20 a30 a21 a31 a22 a32 a23 a33
// b00 b10 b01 b11 b02 b12 b03 b13
// b20 b30 b21 b31 b22 b32 b23 b33
const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
// a00 a10 a20 a30 a01 a11 a21 a31
// b00 b10 b20 b30 b01 b11 b21 b31
// a02 a12 a22 a32 a03 a13 a23 a33
// b02 b12 a22 b32 b03 b13 b23 b33
T0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
T1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
T2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
T3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
// a00 a10 a20 a30 b00 b10 b20 b30
// a01 a11 a21 a31 b01 b11 b21 b31
// a02 a12 a22 a32 b02 b12 b22 b32
// a03 a13 a23 a33 b03 b13 b23 b33
}
// Horizontal pass and subsequent transpose.
{
// First pass, c and d calculations are longer because of the "trick"
// multiplications.
const __m128i four = _mm_set1_epi16(4);
const __m128i dc = _mm_add_epi16(T0, four);
const __m128i a = _mm_add_epi16(dc, T2);
const __m128i b = _mm_sub_epi16(dc, T2);
// c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3
const __m128i c1 = _mm_mulhi_epi16(T1, k2);
const __m128i c2 = _mm_mulhi_epi16(T3, k1);
const __m128i c3 = _mm_sub_epi16(T1, T3);
const __m128i c4 = _mm_sub_epi16(c1, c2);
const __m128i c = _mm_add_epi16(c3, c4);
// d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3
const __m128i d1 = _mm_mulhi_epi16(T1, k1);
const __m128i d2 = _mm_mulhi_epi16(T3, k2);
const __m128i d3 = _mm_add_epi16(T1, T3);
const __m128i d4 = _mm_add_epi16(d1, d2);
const __m128i d = _mm_add_epi16(d3, d4);
// Second pass.
const __m128i tmp0 = _mm_add_epi16(a, d);
const __m128i tmp1 = _mm_add_epi16(b, c);
const __m128i tmp2 = _mm_sub_epi16(b, c);
const __m128i tmp3 = _mm_sub_epi16(a, d);
const __m128i shifted0 = _mm_srai_epi16(tmp0, 3);
const __m128i shifted1 = _mm_srai_epi16(tmp1, 3);
const __m128i shifted2 = _mm_srai_epi16(tmp2, 3);
const __m128i shifted3 = _mm_srai_epi16(tmp3, 3);
// Transpose the two 4x4.
// a00 a01 a02 a03 b00 b01 b02 b03
// a10 a11 a12 a13 b10 b11 b12 b13
// a20 a21 a22 a23 b20 b21 b22 b23
// a30 a31 a32 a33 b30 b31 b32 b33
const __m128i transpose0_0 = _mm_unpacklo_epi16(shifted0, shifted1);
const __m128i transpose0_1 = _mm_unpacklo_epi16(shifted2, shifted3);
const __m128i transpose0_2 = _mm_unpackhi_epi16(shifted0, shifted1);
const __m128i transpose0_3 = _mm_unpackhi_epi16(shifted2, shifted3);
// a00 a10 a01 a11 a02 a12 a03 a13
// a20 a30 a21 a31 a22 a32 a23 a33
// b00 b10 b01 b11 b02 b12 b03 b13
// b20 b30 b21 b31 b22 b32 b23 b33
const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
// a00 a10 a20 a30 a01 a11 a21 a31
// b00 b10 b20 b30 b01 b11 b21 b31
// a02 a12 a22 a32 a03 a13 a23 a33
// b02 b12 a22 b32 b03 b13 b23 b33
T0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
T1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
T2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
T3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
// a00 a10 a20 a30 b00 b10 b20 b30
// a01 a11 a21 a31 b01 b11 b21 b31
// a02 a12 a22 a32 b02 b12 b22 b32
// a03 a13 a23 a33 b03 b13 b23 b33
}
// Add inverse transform to 'ref' and store.
{
const __m128i zero = _mm_set1_epi16(0);
// Load the reference(s).
__m128i ref0, ref1, ref2, ref3;
if (do_two) {
// Load eight bytes/pixels per line.
ref0 = _mm_loadl_epi64((__m128i*)&ref[0 * BPS]);
ref1 = _mm_loadl_epi64((__m128i*)&ref[1 * BPS]);
ref2 = _mm_loadl_epi64((__m128i*)&ref[2 * BPS]);
ref3 = _mm_loadl_epi64((__m128i*)&ref[3 * BPS]);
} else {
// Load four bytes/pixels per line.
ref0 = _mm_cvtsi32_si128(*(int*)&ref[0 * BPS]);
ref1 = _mm_cvtsi32_si128(*(int*)&ref[1 * BPS]);
ref2 = _mm_cvtsi32_si128(*(int*)&ref[2 * BPS]);
ref3 = _mm_cvtsi32_si128(*(int*)&ref[3 * BPS]);
}
// Convert to 16b.
ref0 = _mm_unpacklo_epi8(ref0, zero);
ref1 = _mm_unpacklo_epi8(ref1, zero);
ref2 = _mm_unpacklo_epi8(ref2, zero);
ref3 = _mm_unpacklo_epi8(ref3, zero);
// Add the inverse transform(s).
ref0 = _mm_add_epi16(ref0, T0);
ref1 = _mm_add_epi16(ref1, T1);
ref2 = _mm_add_epi16(ref2, T2);
ref3 = _mm_add_epi16(ref3, T3);
// Unsigned saturate to 8b.
ref0 = _mm_packus_epi16(ref0, ref0);
ref1 = _mm_packus_epi16(ref1, ref1);
ref2 = _mm_packus_epi16(ref2, ref2);
ref3 = _mm_packus_epi16(ref3, ref3);
// Store the results.
if (do_two) {
// Store eight bytes/pixels per line.
_mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0);
_mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1);
_mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2);
_mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3);
} else {
// Store four bytes/pixels per line.
*((int32_t *)&dst[0 * BPS]) = _mm_cvtsi128_si32(ref0);
*((int32_t *)&dst[1 * BPS]) = _mm_cvtsi128_si32(ref1);
*((int32_t *)&dst[2 * BPS]) = _mm_cvtsi128_si32(ref2);
*((int32_t *)&dst[3 * BPS]) = _mm_cvtsi128_si32(ref3);
}
}
}
static void FTransformSSE2(const uint8_t* src, const uint8_t* ref,
int16_t* out) {
const __m128i zero = _mm_setzero_si128();
const __m128i seven = _mm_set1_epi16(7);
const __m128i k7500 = _mm_set1_epi32(7500);
const __m128i k14500 = _mm_set1_epi32(14500);
const __m128i k51000 = _mm_set1_epi32(51000);
const __m128i k12000_plus_one = _mm_set1_epi32(12000 + (1 << 16));
const __m128i k5352_2217 = _mm_set_epi16(5352, 2217, 5352, 2217,
5352, 2217, 5352, 2217);
const __m128i k2217_5352 = _mm_set_epi16(2217, -5352, 2217, -5352,
2217, -5352, 2217, -5352);
__m128i v01, v32;
// Difference between src and ref and initial transpose.
{
// Load src and convert to 16b.
const __m128i src0 = _mm_loadl_epi64((__m128i*)&src[0 * BPS]);
const __m128i src1 = _mm_loadl_epi64((__m128i*)&src[1 * BPS]);
const __m128i src2 = _mm_loadl_epi64((__m128i*)&src[2 * BPS]);
const __m128i src3 = _mm_loadl_epi64((__m128i*)&src[3 * BPS]);
const __m128i src_0 = _mm_unpacklo_epi8(src0, zero);
const __m128i src_1 = _mm_unpacklo_epi8(src1, zero);
const __m128i src_2 = _mm_unpacklo_epi8(src2, zero);
const __m128i src_3 = _mm_unpacklo_epi8(src3, zero);
// Load ref and convert to 16b.
const __m128i ref0 = _mm_loadl_epi64((__m128i*)&ref[0 * BPS]);
const __m128i ref1 = _mm_loadl_epi64((__m128i*)&ref[1 * BPS]);
const __m128i ref2 = _mm_loadl_epi64((__m128i*)&ref[2 * BPS]);
const __m128i ref3 = _mm_loadl_epi64((__m128i*)&ref[3 * BPS]);
const __m128i ref_0 = _mm_unpacklo_epi8(ref0, zero);
const __m128i ref_1 = _mm_unpacklo_epi8(ref1, zero);
const __m128i ref_2 = _mm_unpacklo_epi8(ref2, zero);
const __m128i ref_3 = _mm_unpacklo_epi8(ref3, zero);
// Compute difference.
const __m128i diff0 = _mm_sub_epi16(src_0, ref_0);
const __m128i diff1 = _mm_sub_epi16(src_1, ref_1);
const __m128i diff2 = _mm_sub_epi16(src_2, ref_2);
const __m128i diff3 = _mm_sub_epi16(src_3, ref_3);
// Transpose.
// 00 01 02 03 0 0 0 0
// 10 11 12 13 0 0 0 0
// 20 21 22 23 0 0 0 0
// 30 31 32 33 0 0 0 0
const __m128i transpose0_0 = _mm_unpacklo_epi16(diff0, diff1);
const __m128i transpose0_1 = _mm_unpacklo_epi16(diff2, diff3);
// 00 10 01 11 02 12 03 13
// 20 30 21 31 22 32 23 33
const __m128i v23 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
v01 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
v32 = _mm_shuffle_epi32(v23, _MM_SHUFFLE(1, 0, 3, 2));
// a02 a12 a22 a32 a03 a13 a23 a33
// a00 a10 a20 a30 a01 a11 a21 a31
// a03 a13 a23 a33 a02 a12 a22 a32
}
// First pass and subsequent transpose.
{
// Same operations are done on the (0,3) and (1,2) pairs.
// b0 = (a0 + a3) << 3
// b1 = (a1 + a2) << 3
// b3 = (a0 - a3) << 3
// b2 = (a1 - a2) << 3
const __m128i a01 = _mm_add_epi16(v01, v32);
const __m128i a32 = _mm_sub_epi16(v01, v32);
const __m128i b01 = _mm_slli_epi16(a01, 3);
const __m128i b32 = _mm_slli_epi16(a32, 3);
const __m128i b11 = _mm_unpackhi_epi64(b01, b01);
const __m128i b22 = _mm_unpackhi_epi64(b32, b32);
// e0 = b0 + b1
// e2 = b0 - b1
const __m128i e0 = _mm_add_epi16(b01, b11);
const __m128i e2 = _mm_sub_epi16(b01, b11);
const __m128i e02 = _mm_unpacklo_epi64(e0, e2);
// e1 = (b3 * 5352 + b2 * 2217 + 14500) >> 12
// e3 = (b3 * 2217 - b2 * 5352 + 7500) >> 12
const __m128i b23 = _mm_unpacklo_epi16(b22, b32);
const __m128i c1 = _mm_madd_epi16(b23, k5352_2217);
const __m128i c3 = _mm_madd_epi16(b23, k2217_5352);
const __m128i d1 = _mm_add_epi32(c1, k14500);
const __m128i d3 = _mm_add_epi32(c3, k7500);
const __m128i e1 = _mm_srai_epi32(d1, 12);
const __m128i e3 = _mm_srai_epi32(d3, 12);
const __m128i e13 = _mm_packs_epi32(e1, e3);
// Transpose.
// 00 01 02 03 20 21 22 23
// 10 11 12 13 30 31 32 33
const __m128i transpose0_0 = _mm_unpacklo_epi16(e02, e13);
const __m128i transpose0_1 = _mm_unpackhi_epi16(e02, e13);
// 00 10 01 11 02 12 03 13
// 20 30 21 31 22 32 23 33
const __m128i v23 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
v01 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
v32 = _mm_shuffle_epi32(v23, _MM_SHUFFLE(1, 0, 3, 2));
// 02 12 22 32 03 13 23 33
// 00 10 20 30 01 11 21 31
// 03 13 23 33 02 12 22 32
}
// Second pass
{
// Same operations are done on the (0,3) and (1,2) pairs.
// a0 = v0 + v3
// a1 = v1 + v2
// a3 = v0 - v3
// a2 = v1 - v2
const __m128i a01 = _mm_add_epi16(v01, v32);
const __m128i a32 = _mm_sub_epi16(v01, v32);
const __m128i a11 = _mm_unpackhi_epi64(a01, a01);
const __m128i a22 = _mm_unpackhi_epi64(a32, a32);
// d0 = (a0 + a1 + 7) >> 4;
// d2 = (a0 - a1 + 7) >> 4;
const __m128i b0 = _mm_add_epi16(a01, a11);
const __m128i b2 = _mm_sub_epi16(a01, a11);
const __m128i c0 = _mm_add_epi16(b0, seven);
const __m128i c2 = _mm_add_epi16(b2, seven);
const __m128i d0 = _mm_srai_epi16(c0, 4);
const __m128i d2 = _mm_srai_epi16(c2, 4);
// f1 = ((b3 * 5352 + b2 * 2217 + 12000) >> 16)
// f3 = ((b3 * 2217 - b2 * 5352 + 51000) >> 16)
const __m128i b23 = _mm_unpacklo_epi16(a22, a32);
const __m128i c1 = _mm_madd_epi16(b23, k5352_2217);
const __m128i c3 = _mm_madd_epi16(b23, k2217_5352);
const __m128i d1 = _mm_add_epi32(c1, k12000_plus_one);
const __m128i d3 = _mm_add_epi32(c3, k51000);
const __m128i e1 = _mm_srai_epi32(d1, 16);
const __m128i e3 = _mm_srai_epi32(d3, 16);
const __m128i f1 = _mm_packs_epi32(e1, e1);
const __m128i f3 = _mm_packs_epi32(e3, e3);
// f1 = f1 + (a3 != 0);
// The compare will return (0xffff, 0) for (==0, !=0). To turn that into the
// desired (0, 1), we add one earlier through k12000_plus_one.
const __m128i g1 = _mm_add_epi16(f1, _mm_cmpeq_epi16(a32, zero));
_mm_storel_epi64((__m128i*)&out[ 0], d0);
_mm_storel_epi64((__m128i*)&out[ 4], g1);
_mm_storel_epi64((__m128i*)&out[ 8], d2);
_mm_storel_epi64((__m128i*)&out[12], f3);
}
}
//------------------------------------------------------------------------------
// Metric
static int SSE4x4SSE2(const uint8_t* a, const uint8_t* b) {
const __m128i zero = _mm_set1_epi16(0);
// Load values.
const __m128i a0 = _mm_loadl_epi64((__m128i*)&a[BPS * 0]);
const __m128i a1 = _mm_loadl_epi64((__m128i*)&a[BPS * 1]);
const __m128i a2 = _mm_loadl_epi64((__m128i*)&a[BPS * 2]);
const __m128i a3 = _mm_loadl_epi64((__m128i*)&a[BPS * 3]);
const __m128i b0 = _mm_loadl_epi64((__m128i*)&b[BPS * 0]);
const __m128i b1 = _mm_loadl_epi64((__m128i*)&b[BPS * 1]);
const __m128i b2 = _mm_loadl_epi64((__m128i*)&b[BPS * 2]);
const __m128i b3 = _mm_loadl_epi64((__m128i*)&b[BPS * 3]);
// Combine pair of lines and convert to 16b.
const __m128i a01 = _mm_unpacklo_epi32(a0, a1);
const __m128i a23 = _mm_unpacklo_epi32(a2, a3);
const __m128i b01 = _mm_unpacklo_epi32(b0, b1);
const __m128i b23 = _mm_unpacklo_epi32(b2, b3);
const __m128i a01s = _mm_unpacklo_epi8(a01, zero);
const __m128i a23s = _mm_unpacklo_epi8(a23, zero);
const __m128i b01s = _mm_unpacklo_epi8(b01, zero);
const __m128i b23s = _mm_unpacklo_epi8(b23, zero);
// Compute differences; (a-b)^2 = (abs(a-b))^2 = (sat8(a-b) + sat8(b-a))^2
// TODO(cduvivier): Dissassemble and figure out why this is fastest. We don't
// need absolute values, there is no need to do calculation
// in 8bit as we are already in 16bit, ... Yet this is what
// benchmarks the fastest!
const __m128i d0 = _mm_subs_epu8(a01s, b01s);
const __m128i d1 = _mm_subs_epu8(b01s, a01s);
const __m128i d2 = _mm_subs_epu8(a23s, b23s);
const __m128i d3 = _mm_subs_epu8(b23s, a23s);
// Square and add them all together.
const __m128i madd0 = _mm_madd_epi16(d0, d0);
const __m128i madd1 = _mm_madd_epi16(d1, d1);
const __m128i madd2 = _mm_madd_epi16(d2, d2);
const __m128i madd3 = _mm_madd_epi16(d3, d3);
const __m128i sum0 = _mm_add_epi32(madd0, madd1);
const __m128i sum1 = _mm_add_epi32(madd2, madd3);
const __m128i sum2 = _mm_add_epi32(sum0, sum1);
int32_t tmp[4];
_mm_storeu_si128((__m128i*)tmp, sum2);
return (tmp[3] + tmp[2] + tmp[1] + tmp[0]);
}
//------------------------------------------------------------------------------
// Texture distortion
//
// We try to match the spectral content (weighted) between source and
// reconstructed samples.
// Hadamard transform
// Returns the difference between the weighted sum of the absolute value of
// transformed coefficients.
static int TTransformSSE2(const uint8_t* inA, const uint8_t* inB,
const uint16_t* const w) {
int32_t sum[4];
__m128i tmp_0, tmp_1, tmp_2, tmp_3;
const __m128i zero = _mm_setzero_si128();
const __m128i one = _mm_set1_epi16(1);
const __m128i three = _mm_set1_epi16(3);
// Load, combine and tranpose inputs.
{
const __m128i inA_0 = _mm_loadl_epi64((__m128i*)&inA[BPS * 0]);
const __m128i inA_1 = _mm_loadl_epi64((__m128i*)&inA[BPS * 1]);
const __m128i inA_2 = _mm_loadl_epi64((__m128i*)&inA[BPS * 2]);
const __m128i inA_3 = _mm_loadl_epi64((__m128i*)&inA[BPS * 3]);
const __m128i inB_0 = _mm_loadl_epi64((__m128i*)&inB[BPS * 0]);
const __m128i inB_1 = _mm_loadl_epi64((__m128i*)&inB[BPS * 1]);
const __m128i inB_2 = _mm_loadl_epi64((__m128i*)&inB[BPS * 2]);
const __m128i inB_3 = _mm_loadl_epi64((__m128i*)&inB[BPS * 3]);
// Combine inA and inB (we'll do two transforms in parallel).
const __m128i inAB_0 = _mm_unpacklo_epi8(inA_0, inB_0);
const __m128i inAB_1 = _mm_unpacklo_epi8(inA_1, inB_1);
const __m128i inAB_2 = _mm_unpacklo_epi8(inA_2, inB_2);
const __m128i inAB_3 = _mm_unpacklo_epi8(inA_3, inB_3);
// a00 b00 a01 b01 a02 b03 a03 b03 0 0 0 0 0 0 0 0
// a10 b10 a11 b11 a12 b12 a13 b13 0 0 0 0 0 0 0 0
// a20 b20 a21 b21 a22 b22 a23 b23 0 0 0 0 0 0 0 0
// a30 b30 a31 b31 a32 b32 a33 b33 0 0 0 0 0 0 0 0
// Transpose the two 4x4, discarding the filling zeroes.
const __m128i transpose0_0 = _mm_unpacklo_epi8(inAB_0, inAB_2);
const __m128i transpose0_1 = _mm_unpacklo_epi8(inAB_1, inAB_3);
// a00 a20 b00 b20 a01 a21 b01 b21 a02 a22 b02 b22 a03 a23 b03 b23
// a10 a30 b10 b30 a11 a31 b11 b31 a12 a32 b12 b32 a13 a33 b13 b33
const __m128i transpose1_0 = _mm_unpacklo_epi8(transpose0_0, transpose0_1);
const __m128i transpose1_1 = _mm_unpackhi_epi8(transpose0_0, transpose0_1);
// a00 a10 a20 a30 b00 b10 b20 b30 a01 a11 a21 a31 b01 b11 b21 b31
// a02 a12 a22 a32 b02 b12 b22 b32 a03 a13 a23 a33 b03 b13 b23 b33
// Convert to 16b.
tmp_0 = _mm_unpacklo_epi8(transpose1_0, zero);
tmp_1 = _mm_unpackhi_epi8(transpose1_0, zero);
tmp_2 = _mm_unpacklo_epi8(transpose1_1, zero);
tmp_3 = _mm_unpackhi_epi8(transpose1_1, zero);
// a00 a10 a20 a30 b00 b10 b20 b30
// a01 a11 a21 a31 b01 b11 b21 b31
// a02 a12 a22 a32 b02 b12 b22 b32
// a03 a13 a23 a33 b03 b13 b23 b33
}
// Horizontal pass and subsequent transpose.
{
// Calculate a and b (two 4x4 at once).
const __m128i a0 = _mm_slli_epi16(_mm_add_epi16(tmp_0, tmp_2), 2);
const __m128i a1 = _mm_slli_epi16(_mm_add_epi16(tmp_1, tmp_3), 2);
const __m128i a2 = _mm_slli_epi16(_mm_sub_epi16(tmp_1, tmp_3), 2);
const __m128i a3 = _mm_slli_epi16(_mm_sub_epi16(tmp_0, tmp_2), 2);
// b0_extra = (a0 != 0);
const __m128i b0_extra = _mm_andnot_si128(_mm_cmpeq_epi16 (a0, zero), one);
const __m128i b0_base = _mm_add_epi16(a0, a1);
const __m128i b1 = _mm_add_epi16(a3, a2);
const __m128i b2 = _mm_sub_epi16(a3, a2);
const __m128i b3 = _mm_sub_epi16(a0, a1);
const __m128i b0 = _mm_add_epi16(b0_base, b0_extra);
// a00 a01 a02 a03 b00 b01 b02 b03
// a10 a11 a12 a13 b10 b11 b12 b13
// a20 a21 a22 a23 b20 b21 b22 b23
// a30 a31 a32 a33 b30 b31 b32 b33
// Transpose the two 4x4.
const __m128i transpose0_0 = _mm_unpacklo_epi16(b0, b1);
const __m128i transpose0_1 = _mm_unpacklo_epi16(b2, b3);
const __m128i transpose0_2 = _mm_unpackhi_epi16(b0, b1);
const __m128i transpose0_3 = _mm_unpackhi_epi16(b2, b3);
// a00 a10 a01 a11 a02 a12 a03 a13
// a20 a30 a21 a31 a22 a32 a23 a33
// b00 b10 b01 b11 b02 b12 b03 b13
// b20 b30 b21 b31 b22 b32 b23 b33
const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
// a00 a10 a20 a30 a01 a11 a21 a31
// b00 b10 b20 b30 b01 b11 b21 b31
// a02 a12 a22 a32 a03 a13 a23 a33
// b02 b12 a22 b32 b03 b13 b23 b33
tmp_0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
tmp_1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
tmp_2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
tmp_3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
// a00 a10 a20 a30 b00 b10 b20 b30
// a01 a11 a21 a31 b01 b11 b21 b31
// a02 a12 a22 a32 b02 b12 b22 b32
// a03 a13 a23 a33 b03 b13 b23 b33
}
// Vertical pass and difference of weighted sums.
{
// Load all inputs.
// TODO(cduvivier): Make variable declarations and allocations aligned so
// we can use _mm_load_si128 instead of _mm_loadu_si128.
const __m128i w_0 = _mm_loadu_si128((__m128i*)&w[0]);
const __m128i w_8 = _mm_loadu_si128((__m128i*)&w[8]);
// Calculate a and b (two 4x4 at once).
const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2);
const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3);
const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3);
const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2);
const __m128i b0 = _mm_add_epi16(a0, a1);
const __m128i b1 = _mm_add_epi16(a3, a2);
const __m128i b2 = _mm_sub_epi16(a3, a2);
const __m128i b3 = _mm_sub_epi16(a0, a1);
// Separate the transforms of inA and inB.
__m128i A_b0 = _mm_unpacklo_epi64(b0, b1);
__m128i A_b2 = _mm_unpacklo_epi64(b2, b3);
__m128i B_b0 = _mm_unpackhi_epi64(b0, b1);
__m128i B_b2 = _mm_unpackhi_epi64(b2, b3);
{
// sign(b) = b >> 15 (0x0000 if positive, 0xffff if negative)
const __m128i sign_A_b0 = _mm_srai_epi16(A_b0, 15);
const __m128i sign_A_b2 = _mm_srai_epi16(A_b2, 15);
const __m128i sign_B_b0 = _mm_srai_epi16(B_b0, 15);
const __m128i sign_B_b2 = _mm_srai_epi16(B_b2, 15);
// b = abs(b) = (b ^ sign) - sign
A_b0 = _mm_xor_si128(A_b0, sign_A_b0);
A_b2 = _mm_xor_si128(A_b2, sign_A_b2);
B_b0 = _mm_xor_si128(B_b0, sign_B_b0);
B_b2 = _mm_xor_si128(B_b2, sign_B_b2);
A_b0 = _mm_sub_epi16(A_b0, sign_A_b0);
A_b2 = _mm_sub_epi16(A_b2, sign_A_b2);
B_b0 = _mm_sub_epi16(B_b0, sign_B_b0);
B_b2 = _mm_sub_epi16(B_b2, sign_B_b2);
}
// b = abs(b) + 3
A_b0 = _mm_add_epi16(A_b0, three);
A_b2 = _mm_add_epi16(A_b2, three);
B_b0 = _mm_add_epi16(B_b0, three);
B_b2 = _mm_add_epi16(B_b2, three);
// abs((b + (b<0) + 3) >> 3) = (abs(b) + 3) >> 3
// b = (abs(b) + 3) >> 3
A_b0 = _mm_srai_epi16(A_b0, 3);
A_b2 = _mm_srai_epi16(A_b2, 3);
B_b0 = _mm_srai_epi16(B_b0, 3);
B_b2 = _mm_srai_epi16(B_b2, 3);
// weighted sums
A_b0 = _mm_madd_epi16(A_b0, w_0);
A_b2 = _mm_madd_epi16(A_b2, w_8);
B_b0 = _mm_madd_epi16(B_b0, w_0);
B_b2 = _mm_madd_epi16(B_b2, w_8);
A_b0 = _mm_add_epi32(A_b0, A_b2);
B_b0 = _mm_add_epi32(B_b0, B_b2);
// difference of weighted sums
A_b0 = _mm_sub_epi32(A_b0, B_b0);
_mm_storeu_si128((__m128i*)&sum[0], A_b0);
}
return sum[0] + sum[1] + sum[2] + sum[3];
}
static int Disto4x4SSE2(const uint8_t* const a, const uint8_t* const b,
const uint16_t* const w) {
const int diff_sum = TTransformSSE2(a, b, w);
return (abs(diff_sum) + 8) >> 4;
}
static int Disto16x16SSE2(const uint8_t* const a, const uint8_t* const b,
const uint16_t* const w) {
int D = 0;
int x, y;
for (y = 0; y < 16 * BPS; y += 4 * BPS) {
for (x = 0; x < 16; x += 4) {
D += Disto4x4SSE2(a + x + y, b + x + y, w);
}
}
return D;
}
//------------------------------------------------------------------------------
// Quantization
//
// Simple quantization
static int QuantizeBlockSSE2(int16_t in[16], int16_t out[16],
int n, const VP8Matrix* const mtx) {
const __m128i max_coeff_2047 = _mm_set1_epi16(2047);
const __m128i zero = _mm_set1_epi16(0);
__m128i sign0, sign8;
__m128i coeff0, coeff8;
__m128i out0, out8;
__m128i packed_out;
// Load all inputs.
// TODO(cduvivier): Make variable declarations and allocations aligned so that
// we can use _mm_load_si128 instead of _mm_loadu_si128.
__m128i in0 = _mm_loadu_si128((__m128i*)&in[0]);
__m128i in8 = _mm_loadu_si128((__m128i*)&in[8]);
const __m128i sharpen0 = _mm_loadu_si128((__m128i*)&mtx->sharpen_[0]);
const __m128i sharpen8 = _mm_loadu_si128((__m128i*)&mtx->sharpen_[8]);
const __m128i iq0 = _mm_loadu_si128((__m128i*)&mtx->iq_[0]);
const __m128i iq8 = _mm_loadu_si128((__m128i*)&mtx->iq_[8]);
const __m128i bias0 = _mm_loadu_si128((__m128i*)&mtx->bias_[0]);
const __m128i bias8 = _mm_loadu_si128((__m128i*)&mtx->bias_[8]);
const __m128i q0 = _mm_loadu_si128((__m128i*)&mtx->q_[0]);
const __m128i q8 = _mm_loadu_si128((__m128i*)&mtx->q_[8]);
const __m128i zthresh0 = _mm_loadu_si128((__m128i*)&mtx->zthresh_[0]);
const __m128i zthresh8 = _mm_loadu_si128((__m128i*)&mtx->zthresh_[8]);
// sign(in) = in >> 15 (0x0000 if positive, 0xffff if negative)
sign0 = _mm_srai_epi16(in0, 15);
sign8 = _mm_srai_epi16(in8, 15);
// coeff = abs(in) = (in ^ sign) - sign
coeff0 = _mm_xor_si128(in0, sign0);
coeff8 = _mm_xor_si128(in8, sign8);
coeff0 = _mm_sub_epi16(coeff0, sign0);
coeff8 = _mm_sub_epi16(coeff8, sign8);
// coeff = abs(in) + sharpen
coeff0 = _mm_add_epi16(coeff0, sharpen0);
coeff8 = _mm_add_epi16(coeff8, sharpen8);
// if (coeff > 2047) coeff = 2047
coeff0 = _mm_min_epi16(coeff0, max_coeff_2047);
coeff8 = _mm_min_epi16(coeff8, max_coeff_2047);
// out = (coeff * iQ + B) >> QFIX;
{
// doing calculations with 32b precision (QFIX=17)
// out = (coeff * iQ)
__m128i coeff_iQ0H = _mm_mulhi_epu16(coeff0, iq0);
__m128i coeff_iQ0L = _mm_mullo_epi16(coeff0, iq0);
__m128i coeff_iQ8H = _mm_mulhi_epu16(coeff8, iq8);
__m128i coeff_iQ8L = _mm_mullo_epi16(coeff8, iq8);
__m128i out_00 = _mm_unpacklo_epi16(coeff_iQ0L, coeff_iQ0H);
__m128i out_04 = _mm_unpackhi_epi16(coeff_iQ0L, coeff_iQ0H);
__m128i out_08 = _mm_unpacklo_epi16(coeff_iQ8L, coeff_iQ8H);
__m128i out_12 = _mm_unpackhi_epi16(coeff_iQ8L, coeff_iQ8H);
// expand bias from 16b to 32b
__m128i bias_00 = _mm_unpacklo_epi16(bias0, zero);
__m128i bias_04 = _mm_unpackhi_epi16(bias0, zero);
__m128i bias_08 = _mm_unpacklo_epi16(bias8, zero);
__m128i bias_12 = _mm_unpackhi_epi16(bias8, zero);
// out = (coeff * iQ + B)
out_00 = _mm_add_epi32(out_00, bias_00);
out_04 = _mm_add_epi32(out_04, bias_04);
out_08 = _mm_add_epi32(out_08, bias_08);
out_12 = _mm_add_epi32(out_12, bias_12);
// out = (coeff * iQ + B) >> QFIX;
out_00 = _mm_srai_epi32(out_00, QFIX);
out_04 = _mm_srai_epi32(out_04, QFIX);
out_08 = _mm_srai_epi32(out_08, QFIX);
out_12 = _mm_srai_epi32(out_12, QFIX);
// pack result as 16b
out0 = _mm_packs_epi32(out_00, out_04);
out8 = _mm_packs_epi32(out_08, out_12);
}
// get sign back (if (sign[j]) out_n = -out_n)
out0 = _mm_xor_si128(out0, sign0);
out8 = _mm_xor_si128(out8, sign8);
out0 = _mm_sub_epi16(out0, sign0);
out8 = _mm_sub_epi16(out8, sign8);
// in = out * Q
in0 = _mm_mullo_epi16(out0, q0);
in8 = _mm_mullo_epi16(out8, q8);
// if (coeff <= mtx->zthresh_) {in=0; out=0;}
{
__m128i cmp0 = _mm_cmpgt_epi16(coeff0, zthresh0);
__m128i cmp8 = _mm_cmpgt_epi16(coeff8, zthresh8);
in0 = _mm_and_si128(in0, cmp0);
in8 = _mm_and_si128(in8, cmp8);
_mm_storeu_si128((__m128i*)&in[0], in0);
_mm_storeu_si128((__m128i*)&in[8], in8);
out0 = _mm_and_si128(out0, cmp0);
out8 = _mm_and_si128(out8, cmp8);
}
// zigzag the output before storing it.
//
// The zigzag pattern can almost be reproduced with a small sequence of
// shuffles. After it, we only need to swap the 7th (ending up in third
// position instead of twelfth) and 8th values.
{
__m128i outZ0, outZ8;
outZ0 = _mm_shufflehi_epi16(out0, _MM_SHUFFLE(2, 1, 3, 0));
outZ0 = _mm_shuffle_epi32 (outZ0, _MM_SHUFFLE(3, 1, 2, 0));
outZ0 = _mm_shufflehi_epi16(outZ0, _MM_SHUFFLE(3, 1, 0, 2));
outZ8 = _mm_shufflelo_epi16(out8, _MM_SHUFFLE(3, 0, 2, 1));
outZ8 = _mm_shuffle_epi32 (outZ8, _MM_SHUFFLE(3, 1, 2, 0));
outZ8 = _mm_shufflelo_epi16(outZ8, _MM_SHUFFLE(1, 3, 2, 0));
_mm_storeu_si128((__m128i*)&out[0], outZ0);
_mm_storeu_si128((__m128i*)&out[8], outZ8);
packed_out = _mm_packs_epi16(outZ0, outZ8);
}
{
const int16_t outZ_12 = out[12];
const int16_t outZ_3 = out[3];
out[3] = outZ_12;
out[12] = outZ_3;
}
// detect if all 'out' values are zeroes or not
{
int32_t tmp[4];
_mm_storeu_si128((__m128i*)tmp, packed_out);
if (n) {
tmp[0] &= ~0xff;
}
return (tmp[3] || tmp[2] || tmp[1] || tmp[0]);
}
}
extern void VP8EncDspInitSSE2(void);
void VP8EncDspInitSSE2(void) {
VP8CollectHistogram = CollectHistogramSSE2;
VP8EncQuantizeBlock = QuantizeBlockSSE2;
VP8ITransform = ITransformSSE2;
VP8FTransform = FTransformSSE2;
VP8SSE4x4 = SSE4x4SSE2;
VP8TDisto4x4 = Disto4x4SSE2;
VP8TDisto16x16 = Disto16x16SSE2;
}
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
#endif //__SSE2__

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,8 +9,7 @@
// //
// Author: somnath@google.com (Somnath Banerjee) // Author: somnath@google.com (Somnath Banerjee)
#include <math.h> #include "./vp8enci.h"
#include "vp8enci.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -49,7 +48,7 @@ static void InitTables(void) {
// Edge filtering functions // Edge filtering functions
// 4 pixels in, 2 pixels out // 4 pixels in, 2 pixels out
static inline void do_filter2(uint8_t* p, int step) { static WEBP_INLINE void do_filter2(uint8_t* p, int step) {
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1]; const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1];
const int a1 = sclip2[112 + ((a + 4) >> 3)]; const int a1 = sclip2[112 + ((a + 4) >> 3)];
@ -59,7 +58,7 @@ static inline void do_filter2(uint8_t* p, int step) {
} }
// 4 pixels in, 4 pixels out // 4 pixels in, 4 pixels out
static inline void do_filter4(uint8_t* p, int step) { static WEBP_INLINE void do_filter4(uint8_t* p, int step) {
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
const int a = 3 * (q0 - p0); const int a = 3 * (q0 - p0);
const int a1 = sclip2[112 + ((a + 4) >> 3)]; const int a1 = sclip2[112 + ((a + 4) >> 3)];
@ -72,17 +71,18 @@ static inline void do_filter4(uint8_t* p, int step) {
} }
// high edge-variance // high edge-variance
static inline int hev(const uint8_t* p, int step, int thresh) { static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) {
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh); return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh);
} }
static inline int needs_filter(const uint8_t* p, int step, int thresh) { static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int thresh) {
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh; return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh;
} }
static inline int needs_filter2(const uint8_t* p, int step, int t, int it) { static WEBP_INLINE int needs_filter2(const uint8_t* p,
int step, int t, int it) {
const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step]; const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step]; const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step];
if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t) if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t)
@ -132,8 +132,9 @@ static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Complex In-loop filtering (Paragraph 15.3) // Complex In-loop filtering (Paragraph 15.3)
static inline void FilterLoop24(uint8_t* p, int hstride, int vstride, int size, static WEBP_INLINE void FilterLoop24(uint8_t* p,
int thresh, int ithresh, int hev_thresh) { int hstride, int vstride, int size,
int thresh, int ithresh, int hev_thresh) {
while (size-- > 0) { while (size-- > 0) {
if (needs_filter2(p, hstride, thresh, ithresh)) { if (needs_filter2(p, hstride, thresh, ithresh)) {
if (hev(p, hstride, hev_thresh)) { if (hev(p, hstride, hev_thresh)) {
@ -233,14 +234,21 @@ static void DoFilter(const VP8EncIterator* const it, int level) {
// SSIM metric // SSIM metric
enum { KERNEL = 3 }; enum { KERNEL = 3 };
typedef struct { static const double kMinValue = 1.e-10; // minimal threshold
double w, xm, ym, xxm, xym, yym;
} SSIMStats;
static void Accumulate(const uint8_t* src1, int stride1, void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst) {
const uint8_t* src2, int stride2, dst->w += src->w;
int xo, int yo, int W, int H, dst->xm += src->xm;
SSIMStats* const stats) { dst->ym += src->ym;
dst->xxm += src->xxm;
dst->xym += src->xym;
dst->yym += src->yym;
}
static void VP8SSIMAccumulate(const uint8_t* src1, int stride1,
const uint8_t* src2, int stride2,
int xo, int yo, int W, int H,
DistoStats* const stats) {
const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL; const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL;
const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL; const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL;
const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL; const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL;
@ -262,7 +270,7 @@ static void Accumulate(const uint8_t* src1, int stride1,
} }
} }
static double GetSSIM(const SSIMStats* const stats) { double VP8SSIMGet(const DistoStats* const stats) {
const double xmxm = stats->xm * stats->xm; const double xmxm = stats->xm * stats->xm;
const double ymym = stats->ym * stats->ym; const double ymym = stats->ym * stats->ym;
const double xmym = stats->xm * stats->ym; const double xmym = stats->xm * stats->ym;
@ -280,26 +288,49 @@ static double GetSSIM(const SSIMStats* const stats) {
C2 = 58.5225 * w2; C2 = 58.5225 * w2;
fnum = (2 * xmym + C1) * (2 * sxy + C2); fnum = (2 * xmym + C1) * (2 * sxy + C2);
fden = (xmxm + ymym + C1) * (sxx + syy + C2); fden = (xmxm + ymym + C1) * (sxx + syy + C2);
return (fden != 0) ? fnum / fden : 0.; return (fden != 0.) ? fnum / fden : kMinValue;
}
double VP8SSIMGetSquaredError(const DistoStats* const s) {
if (s->w > 0.) {
const double iw2 = 1. / (s->w * s->w);
const double sxx = s->xxm * s->w - s->xm * s->xm;
const double syy = s->yym * s->w - s->ym * s->ym;
const double sxy = s->xym * s->w - s->xm * s->ym;
const double SSE = iw2 * (sxx + syy - 2. * sxy);
if (SSE > kMinValue) return SSE;
}
return kMinValue;
}
void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1,
const uint8_t* src2, int stride2,
int W, int H, DistoStats* const stats) {
int x, y;
for (y = 0; y < H; ++y) {
for (x = 0; x < W; ++x) {
VP8SSIMAccumulate(src1, stride1, src2, stride2, x, y, W, H, stats);
}
}
} }
static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) { static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) {
int x, y; int x, y;
SSIMStats s = { .0, .0, .0, .0, .0, .0 }; DistoStats s = { .0, .0, .0, .0, .0, .0 };
// compute SSIM in a 10 x 10 window // compute SSIM in a 10 x 10 window
for (x = 3; x < 13; x++) { for (x = 3; x < 13; x++) {
for (y = 3; y < 13; y++) { for (y = 3; y < 13; y++) {
Accumulate(yuv1 + Y_OFF, BPS, yuv2 + Y_OFF, BPS, x, y, 16, 16, &s); VP8SSIMAccumulate(yuv1 + Y_OFF, BPS, yuv2 + Y_OFF, BPS, x, y, 16, 16, &s);
} }
} }
for (x = 1; x < 7; x++) { for (x = 1; x < 7; x++) {
for (y = 1; y < 7; y++) { for (y = 1; y < 7; y++) {
Accumulate(yuv1 + U_OFF, BPS, yuv2 + U_OFF, BPS, x, y, 8, 8, &s); VP8SSIMAccumulate(yuv1 + U_OFF, BPS, yuv2 + U_OFF, BPS, x, y, 8, 8, &s);
Accumulate(yuv1 + V_OFF, BPS, yuv2 + V_OFF, BPS, x, y, 8, 8, &s); VP8SSIMAccumulate(yuv1 + V_OFF, BPS, yuv2 + V_OFF, BPS, x, y, 8, 8, &s);
} }
} }
return GetSSIM(&s); return VP8SSIMGet(&s);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,13 +9,13 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include <math.h> #include <math.h>
#include "vp8enci.h" #include "./vp8enci.h"
#include "cost.h" #include "./cost.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -54,15 +54,17 @@ static const uint8_t kCat6[] =
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Reset the statistics about: number of skips, token proba, level cost,... // Reset the statistics about: number of skips, token proba, level cost,...
static void ResetStats(VP8Encoder* const enc, int precalc_cost) { static void ResetStats(VP8Encoder* const enc) {
VP8Proba* const proba = &enc->proba_; VP8Proba* const proba = &enc->proba_;
if (precalc_cost) VP8CalculateLevelCosts(proba); VP8CalculateLevelCosts(proba);
proba->nb_skip_ = 0; proba->nb_skip_ = 0;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Skip decision probability // Skip decision probability
#define SKIP_PROBA_THRESHOLD 250 // value below which using skip_proba is OK.
static int CalcSkipProba(uint64_t nb, uint64_t total) { static int CalcSkipProba(uint64_t nb, uint64_t total) {
return (int)(total ? (total - nb) * 255 / total : 255); return (int)(total ? (total - nb) * 255 / total : 255);
} }
@ -74,7 +76,7 @@ static int FinalizeSkipProba(VP8Encoder* const enc) {
const int nb_events = proba->nb_skip_; const int nb_events = proba->nb_skip_;
int size; int size;
proba->skip_proba_ = CalcSkipProba(nb_events, nb_mbs); proba->skip_proba_ = CalcSkipProba(nb_events, nb_mbs);
proba->use_skip_proba_ = (proba->skip_proba_ < 250); proba->use_skip_proba_ = (proba->skip_proba_ < SKIP_PROBA_THRESHOLD);
size = 256; // 'use_skip_proba' bit size = 256; // 'use_skip_proba' bit
if (proba->use_skip_proba_) { if (proba->use_skip_proba_) {
size += nb_events * VP8BitCost(1, proba->skip_proba_) size += nb_events * VP8BitCost(1, proba->skip_proba_)
@ -93,9 +95,14 @@ static void ResetTokenStats(VP8Encoder* const enc) {
} }
// Record proba context used // Record proba context used
static int Record(int bit, uint64_t* const stats) { static int Record(int bit, proba_t* const stats) {
stats[0] += bit; proba_t p = *stats;
stats[1] += 1; if (p >= 0xffff0000u) { // an overflow is inbound.
p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2.
}
// record bit count (lower 16 bits) and increment total count (upper 16 bits).
p += 0x00010000u + bit;
*stats = p;
return bit; return bit;
} }
@ -104,33 +111,35 @@ static int Record(int bit, uint64_t* const stats) {
// Simulate block coding, but only record statistics. // Simulate block coding, but only record statistics.
// Note: no need to record the fixed probas. // Note: no need to record the fixed probas.
static int RecordCoeffs(int ctx, VP8Residual* res) { static int RecordCoeffs(int ctx, const VP8Residual* const res) {
int n = res->first; int n = res->first;
uint64_t (*s)[2] = res->stats[VP8EncBands[n]][ctx]; proba_t* s = res->stats[VP8EncBands[n]][ctx];
if (!Record(res->last >= 0, s[0])) { if (res->last < 0) {
Record(0, s + 0);
return 0; return 0;
} }
while (n <= res->last) {
while (1) { int v;
int v = res->coeffs[n++]; Record(1, s + 0);
if (!Record(v != 0, s[1])) { while ((v = res->coeffs[n++]) == 0) {
Record(0, s + 1);
s = res->stats[VP8EncBands[n]][0]; s = res->stats[VP8EncBands[n]][0];
continue;
} }
if (!Record(2u < (unsigned int)(v + 1), s[2])) { // v = -1 or 1 Record(1, s + 1);
if (!Record(2u < (unsigned int)(v + 1), s + 2)) { // v = -1 or 1
s = res->stats[VP8EncBands[n]][1]; s = res->stats[VP8EncBands[n]][1];
} else { } else {
v = abs(v); v = abs(v);
#if !defined(USE_LEVEL_CODE_TABLE) #if !defined(USE_LEVEL_CODE_TABLE)
if (!Record(v > 4, s[3])) { if (!Record(v > 4, s + 3)) {
if (Record(v != 2, s[4])) if (Record(v != 2, s + 4))
Record(v == 4, s[5]); Record(v == 4, s + 5);
} else if (!Record(v > 10, s[6])) { } else if (!Record(v > 10, s + 6)) {
Record(v > 6, s[7]); Record(v > 6, s + 7);
} else if (!Record((v >= 3 + (8 << 2)), s[8])) { } else if (!Record((v >= 3 + (8 << 2)), s + 8)) {
Record((v >= 3 + (8 << 1)), s[9]); Record((v >= 3 + (8 << 1)), s + 9);
} else { } else {
Record((v >= 3 + (8 << 3)), s[10]); Record((v >= 3 + (8 << 3)), s + 10);
} }
#else #else
if (v > MAX_VARIABLE_LEVEL) if (v > MAX_VARIABLE_LEVEL)
@ -142,44 +151,54 @@ static int RecordCoeffs(int ctx, VP8Residual* res) {
int i; int i;
for (i = 0; (pattern >>= 1) != 0; ++i) { for (i = 0; (pattern >>= 1) != 0; ++i) {
const int mask = 2 << i; const int mask = 2 << i;
if (pattern & 1) Record(!!(bits & mask), s[3 + i]); if (pattern & 1) Record(!!(bits & mask), s + 3 + i);
} }
} }
#endif #endif
s = res->stats[VP8EncBands[n]][2]; s = res->stats[VP8EncBands[n]][2];
} }
if (n == 16 || !Record(n <= res->last, s[0])) {
return 1;
}
} }
if (n < 16) Record(0, s + 0);
return 1;
} }
// Collect statistics and deduce probabilities for next coding pass. // Collect statistics and deduce probabilities for next coding pass.
// Return the total bit-cost for coding the probability updates. // Return the total bit-cost for coding the probability updates.
static int CalcTokenProba(uint64_t nb, uint64_t total) { static int CalcTokenProba(int nb, int total) {
return (int)(nb ? ((total - nb) * 255 + total / 2) / total : 255); assert(nb <= total);
return nb ? (255 - nb * 255 / total) : 255;
}
// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability.
static int BranchCost(int nb, int total, int proba) {
return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba);
} }
static int FinalizeTokenProbas(VP8Encoder* const enc) { static int FinalizeTokenProbas(VP8Encoder* const enc) {
VP8Proba* const proba = &enc->proba_; VP8Proba* const proba = &enc->proba_;
int has_changed = 0;
int size = 0; int size = 0;
int t, b, c, p; int t, b, c, p;
for (t = 0; t < NUM_TYPES; ++t) { for (t = 0; t < NUM_TYPES; ++t) {
for (b = 0; b < NUM_BANDS; ++b) { for (b = 0; b < NUM_BANDS; ++b) {
for (c = 0; c < NUM_CTX; ++c) { for (c = 0; c < NUM_CTX; ++c) {
for (p = 0; p < NUM_PROBAS; ++p) { for (p = 0; p < NUM_PROBAS; ++p) {
const uint64_t* const cnt = proba->stats_[t][b][c][p]; const proba_t stats = proba->stats_[t][b][c][p];
const int nb = (stats >> 0) & 0xffff;
const int total = (stats >> 16) & 0xffff;
const int update_proba = VP8CoeffsUpdateProba[t][b][c][p]; const int update_proba = VP8CoeffsUpdateProba[t][b][c][p];
const int old_p = VP8CoeffsProba0[t][b][c][p]; const int old_p = VP8CoeffsProba0[t][b][c][p];
const int new_p = CalcTokenProba(cnt[0], cnt[1]); const int new_p = CalcTokenProba(nb, total);
const uint64_t old_cost = VP8BranchCost(cnt[0], cnt[1], old_p) const int old_cost = BranchCost(nb, total, old_p)
+ VP8BitCost(0, update_proba); + VP8BitCost(0, update_proba);
const uint64_t new_cost = VP8BranchCost(cnt[0], cnt[1], new_p) const int new_cost = BranchCost(nb, total, new_p)
+ VP8BitCost(1, update_proba) + 8 * 256; + VP8BitCost(1, update_proba)
+ 8 * 256;
const int use_new_p = (old_cost > new_cost); const int use_new_p = (old_cost > new_cost);
size += VP8BitCost(use_new_p, update_proba); size += VP8BitCost(use_new_p, update_proba);
if (use_new_p) { // only use proba that seem meaningful enough. if (use_new_p) { // only use proba that seem meaningful enough.
proba->coeffs_[t][b][c][p] = new_p; proba->coeffs_[t][b][c][p] = new_p;
has_changed |= (new_p != old_p);
size += 8 * 256; size += 8 * 256;
} else { } else {
proba->coeffs_[t][b][c][p] = old_p; proba->coeffs_[t][b][c][p] = old_p;
@ -188,6 +207,7 @@ static int FinalizeTokenProbas(VP8Encoder* const enc) {
} }
} }
} }
proba->dirty_ = has_changed;
return size; return size;
} }
@ -221,44 +241,48 @@ static void SetResidualCoeffs(const int16_t* const coeffs,
static int GetResidualCost(int ctx, const VP8Residual* const res) { static int GetResidualCost(int ctx, const VP8Residual* const res) {
int n = res->first; int n = res->first;
const uint8_t* p = res->prob[VP8EncBands[n]][ctx]; int p0 = res->prob[VP8EncBands[n]][ctx][0];
const uint16_t *t = res->cost[VP8EncBands[n]][ctx]; const uint16_t* t = res->cost[VP8EncBands[n]][ctx];
int cost; int cost;
cost = VP8BitCost(res->last >= 0, p[0]);
if (res->last < 0) { if (res->last < 0) {
return cost; return VP8BitCost(0, p0);
} }
cost = 0;
while (n <= res->last) { while (n <= res->last) {
const int v = res->coeffs[n++]; const int v = res->coeffs[n];
const int b = VP8EncBands[n + 1];
++n;
if (v == 0) { if (v == 0) {
cost += VP8LevelCost(t, 0); // short-case for VP8LevelCost(t, 0) (note: VP8LevelFixedCosts[0] == 0):
p = res->prob[VP8EncBands[n]][0]; cost += t[0];
t = res->cost[VP8EncBands[n]][0]; t = res->cost[b][0];
continue; continue;
} else if (2u >= (unsigned int)(v + 1)) { // v = -1 or 1 }
cost += VP8LevelCost(t, 1); cost += VP8BitCost(1, p0);
p = res->prob[VP8EncBands[n]][1]; if (2u >= (unsigned int)(v + 1)) { // v = -1 or 1
t = res->cost[VP8EncBands[n]][1]; // short-case for "VP8LevelCost(t, 1)" (256 is VP8LevelFixedCosts[1]):
cost += 256 + t[1];
p0 = res->prob[b][1][0];
t = res->cost[b][1];
} else { } else {
cost += VP8LevelCost(t, abs(v)); cost += VP8LevelCost(t, abs(v));
p = res->prob[VP8EncBands[n]][2]; p0 = res->prob[b][2][0];
t = res->cost[VP8EncBands[n]][2]; t = res->cost[b][2];
}
if (n < 16) {
cost += VP8BitCost(n <= res->last, p[0]);
} }
} }
if (n < 16) cost += VP8BitCost(0, p0);
return cost; return cost;
} }
int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) { int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) {
const int x = (it->i4_ & 3), y = (it->i4_ >> 2); const int x = (it->i4_ & 3), y = (it->i4_ >> 2);
VP8Residual res; VP8Residual res;
VP8Encoder* const enc = it->enc_;
int R = 0; int R = 0;
int ctx; int ctx;
InitResidual(0, 3, it->enc_, &res); InitResidual(0, 3, enc, &res);
ctx = it->top_nz_[x] + it->left_nz_[y]; ctx = it->top_nz_[x] + it->left_nz_[y];
SetResidualCoeffs(levels, &res); SetResidualCoeffs(levels, &res);
R += GetResidualCost(ctx, &res); R += GetResidualCost(ctx, &res);
@ -267,18 +291,19 @@ int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) {
int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) { int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) {
VP8Residual res; VP8Residual res;
VP8Encoder* const enc = it->enc_;
int x, y; int x, y;
int R = 0; int R = 0;
VP8IteratorNzToBytes(it); // re-import the non-zero context VP8IteratorNzToBytes(it); // re-import the non-zero context
// DC // DC
InitResidual(0, 1, it->enc_, &res); InitResidual(0, 1, enc, &res);
SetResidualCoeffs(rd->y_dc_levels, &res); SetResidualCoeffs(rd->y_dc_levels, &res);
R += GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &res); R += GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &res);
// AC // AC
InitResidual(1, 0, it->enc_, &res); InitResidual(1, 0, enc, &res);
for (y = 0; y < 4; ++y) { for (y = 0; y < 4; ++y) {
for (x = 0; x < 4; ++x) { for (x = 0; x < 4; ++x) {
const int ctx = it->top_nz_[x] + it->left_nz_[y]; const int ctx = it->top_nz_[x] + it->left_nz_[y];
@ -292,12 +317,13 @@ int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) {
int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) { int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) {
VP8Residual res; VP8Residual res;
VP8Encoder* const enc = it->enc_;
int ch, x, y; int ch, x, y;
int R = 0; int R = 0;
VP8IteratorNzToBytes(it); // re-import the non-zero context VP8IteratorNzToBytes(it); // re-import the non-zero context
InitResidual(0, 2, it->enc_, &res); InitResidual(0, 2, enc, &res);
for (ch = 0; ch <= 2; ch += 2) { for (ch = 0; ch <= 2; ch += 2) {
for (y = 0; y < 2; ++y) { for (y = 0; y < 2; ++y) {
for (x = 0; x < 2; ++x) { for (x = 0; x < 2; ++x) {
@ -393,18 +419,19 @@ static void CodeResiduals(VP8BitWriter* const bw,
uint64_t pos1, pos2, pos3; uint64_t pos1, pos2, pos3;
const int i16 = (it->mb_->type_ == 1); const int i16 = (it->mb_->type_ == 1);
const int segment = it->mb_->segment_; const int segment = it->mb_->segment_;
VP8Encoder* const enc = it->enc_;
VP8IteratorNzToBytes(it); VP8IteratorNzToBytes(it);
pos1 = VP8BitWriterPos(bw); pos1 = VP8BitWriterPos(bw);
if (i16) { if (i16) {
InitResidual(0, 1, it->enc_, &res); InitResidual(0, 1, enc, &res);
SetResidualCoeffs(rd->y_dc_levels, &res); SetResidualCoeffs(rd->y_dc_levels, &res);
it->top_nz_[8] = it->left_nz_[8] = it->top_nz_[8] = it->left_nz_[8] =
PutCoeffs(bw, it->top_nz_[8] + it->left_nz_[8], &res); PutCoeffs(bw, it->top_nz_[8] + it->left_nz_[8], &res);
InitResidual(1, 0, it->enc_, &res); InitResidual(1, 0, enc, &res);
} else { } else {
InitResidual(0, 3, it->enc_, &res); InitResidual(0, 3, enc, &res);
} }
// luma-AC // luma-AC
@ -418,7 +445,7 @@ static void CodeResiduals(VP8BitWriter* const bw,
pos2 = VP8BitWriterPos(bw); pos2 = VP8BitWriterPos(bw);
// U/V // U/V
InitResidual(0, 2, it->enc_, &res); InitResidual(0, 2, enc, &res);
for (ch = 0; ch <= 2; ch += 2) { for (ch = 0; ch <= 2; ch += 2) {
for (y = 0; y < 2; ++y) { for (y = 0; y < 2; ++y) {
for (x = 0; x < 2; ++x) { for (x = 0; x < 2; ++x) {
@ -443,17 +470,18 @@ static void RecordResiduals(VP8EncIterator* const it,
const VP8ModeScore* const rd) { const VP8ModeScore* const rd) {
int x, y, ch; int x, y, ch;
VP8Residual res; VP8Residual res;
VP8Encoder* const enc = it->enc_;
VP8IteratorNzToBytes(it); VP8IteratorNzToBytes(it);
if (it->mb_->type_ == 1) { // i16x16 if (it->mb_->type_ == 1) { // i16x16
InitResidual(0, 1, it->enc_, &res); InitResidual(0, 1, enc, &res);
SetResidualCoeffs(rd->y_dc_levels, &res); SetResidualCoeffs(rd->y_dc_levels, &res);
it->top_nz_[8] = it->left_nz_[8] = it->top_nz_[8] = it->left_nz_[8] =
RecordCoeffs(it->top_nz_[8] + it->left_nz_[8], &res); RecordCoeffs(it->top_nz_[8] + it->left_nz_[8], &res);
InitResidual(1, 0, it->enc_, &res); InitResidual(1, 0, enc, &res);
} else { } else {
InitResidual(0, 3, it->enc_, &res); InitResidual(0, 3, enc, &res);
} }
// luma-AC // luma-AC
@ -466,7 +494,7 @@ static void RecordResiduals(VP8EncIterator* const it,
} }
// U/V // U/V
InitResidual(0, 2, it->enc_, &res); InitResidual(0, 2, enc, &res);
for (ch = 0; ch <= 2; ch += 2) { for (ch = 0; ch <= 2; ch += 2) {
for (y = 0; y < 2; ++y) { for (y = 0; y < 2; ++y) {
for (x = 0; x < 2; ++x) { for (x = 0; x < 2; ++x) {
@ -481,6 +509,180 @@ static void RecordResiduals(VP8EncIterator* const it,
VP8IteratorBytesToNz(it); VP8IteratorBytesToNz(it);
} }
//------------------------------------------------------------------------------
// Token buffer
#ifdef USE_TOKEN_BUFFER
void VP8TBufferInit(VP8TBuffer* const b) {
b->rows_ = NULL;
b->tokens_ = NULL;
b->last_ = &b->rows_;
b->left_ = 0;
b->error_ = 0;
}
int VP8TBufferNewPage(VP8TBuffer* const b) {
VP8Tokens* const page = b->error_ ? NULL : (VP8Tokens*)malloc(sizeof(*page));
if (page == NULL) {
b->error_ = 1;
return 0;
}
*b->last_ = page;
b->last_ = &page->next_;
b->left_ = MAX_NUM_TOKEN;
b->tokens_ = page->tokens_;
return 1;
}
void VP8TBufferClear(VP8TBuffer* const b) {
if (b != NULL) {
const VP8Tokens* p = b->rows_;
while (p != NULL) {
const VP8Tokens* const next = p->next_;
free((void*)p);
p = next;
}
VP8TBufferInit(b);
}
}
int VP8EmitTokens(const VP8TBuffer* const b, VP8BitWriter* const bw,
const uint8_t* const probas) {
VP8Tokens* p = b->rows_;
if (b->error_) return 0;
while (p != NULL) {
const int N = (p->next_ == NULL) ? b->left_ : 0;
int n = MAX_NUM_TOKEN;
while (n-- > N) {
VP8PutBit(bw, (p->tokens_[n] >> 15) & 1, probas[p->tokens_[n] & 0x7fff]);
}
p = p->next_;
}
return 1;
}
#define TOKEN_ID(b, ctx, p) ((p) + NUM_PROBAS * ((ctx) + (b) * NUM_CTX))
static int RecordCoeffTokens(int ctx, const VP8Residual* const res,
VP8TBuffer* tokens) {
int n = res->first;
int b = VP8EncBands[n];
if (!VP8AddToken(tokens, res->last >= 0, TOKEN_ID(b, ctx, 0))) {
return 0;
}
while (n < 16) {
const int c = res->coeffs[n++];
const int sign = c < 0;
int v = sign ? -c : c;
const int base_id = TOKEN_ID(b, ctx, 0);
if (!VP8AddToken(tokens, v != 0, base_id + 1)) {
b = VP8EncBands[n];
ctx = 0;
continue;
}
if (!VP8AddToken(tokens, v > 1, base_id + 2)) {
b = VP8EncBands[n];
ctx = 1;
} else {
if (!VP8AddToken(tokens, v > 4, base_id + 3)) {
if (VP8AddToken(tokens, v != 2, base_id + 4))
VP8AddToken(tokens, v == 4, base_id + 5);
} else if (!VP8AddToken(tokens, v > 10, base_id + 6)) {
if (!VP8AddToken(tokens, v > 6, base_id + 7)) {
// VP8AddToken(tokens, v == 6, 159);
} else {
// VP8AddToken(tokens, v >= 9, 165);
// VP8AddToken(tokens, !(v & 1), 145);
}
} else {
int mask;
const uint8_t* tab;
if (v < 3 + (8 << 1)) { // kCat3 (3b)
VP8AddToken(tokens, 0, base_id + 8);
VP8AddToken(tokens, 0, base_id + 9);
v -= 3 + (8 << 0);
mask = 1 << 2;
tab = kCat3;
} else if (v < 3 + (8 << 2)) { // kCat4 (4b)
VP8AddToken(tokens, 0, base_id + 8);
VP8AddToken(tokens, 1, base_id + 9);
v -= 3 + (8 << 1);
mask = 1 << 3;
tab = kCat4;
} else if (v < 3 + (8 << 3)) { // kCat5 (5b)
VP8AddToken(tokens, 1, base_id + 8);
VP8AddToken(tokens, 0, base_id + 10);
v -= 3 + (8 << 2);
mask = 1 << 4;
tab = kCat5;
} else { // kCat6 (11b)
VP8AddToken(tokens, 1, base_id + 8);
VP8AddToken(tokens, 1, base_id + 10);
v -= 3 + (8 << 3);
mask = 1 << 10;
tab = kCat6;
}
while (mask) {
// VP8AddToken(tokens, !!(v & mask), *tab++);
mask >>= 1;
}
}
ctx = 2;
}
b = VP8EncBands[n];
// VP8PutBitUniform(bw, sign);
if (n == 16 || !VP8AddToken(tokens, n <= res->last, TOKEN_ID(b, ctx, 0))) {
return 1; // EOB
}
}
return 1;
}
static void RecordTokens(VP8EncIterator* const it,
const VP8ModeScore* const rd, VP8TBuffer tokens[2]) {
int x, y, ch;
VP8Residual res;
VP8Encoder* const enc = it->enc_;
VP8IteratorNzToBytes(it);
if (it->mb_->type_ == 1) { // i16x16
InitResidual(0, 1, enc, &res);
SetResidualCoeffs(rd->y_dc_levels, &res);
// TODO(skal): FIX -> it->top_nz_[8] = it->left_nz_[8] =
RecordCoeffTokens(it->top_nz_[8] + it->left_nz_[8], &res, &tokens[0]);
InitResidual(1, 0, enc, &res);
} else {
InitResidual(0, 3, enc, &res);
}
// luma-AC
for (y = 0; y < 4; ++y) {
for (x = 0; x < 4; ++x) {
const int ctx = it->top_nz_[x] + it->left_nz_[y];
SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
it->top_nz_[x] = it->left_nz_[y] =
RecordCoeffTokens(ctx, &res, &tokens[0]);
}
}
// U/V
InitResidual(0, 2, enc, &res);
for (ch = 0; ch <= 2; ch += 2) {
for (y = 0; y < 2; ++y) {
for (x = 0; x < 2; ++x) {
const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
RecordCoeffTokens(ctx, &res, &tokens[1]);
}
}
}
}
#endif // USE_TOKEN_BUFFER
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// ExtraInfo map / Debug function // ExtraInfo map / Debug function
@ -515,16 +717,16 @@ static void StoreSideInfo(const VP8EncIterator* const it) {
const VP8MBInfo* const mb = it->mb_; const VP8MBInfo* const mb = it->mb_;
WebPPicture* const pic = enc->pic_; WebPPicture* const pic = enc->pic_;
if (pic->stats) { if (pic->stats != NULL) {
StoreSSE(it); StoreSSE(it);
enc->block_count_[0] += (mb->type_ == 0); enc->block_count_[0] += (mb->type_ == 0);
enc->block_count_[1] += (mb->type_ == 1); enc->block_count_[1] += (mb->type_ == 1);
enc->block_count_[2] += (mb->skip_ != 0); enc->block_count_[2] += (mb->skip_ != 0);
} }
if (pic->extra_info) { if (pic->extra_info != NULL) {
uint8_t* const info = &pic->extra_info[it->x_ + it->y_ * enc->mb_w_]; uint8_t* const info = &pic->extra_info[it->x_ + it->y_ * enc->mb_w_];
switch(pic->extra_info_type) { switch (pic->extra_info_type) {
case 1: *info = mb->type_; break; case 1: *info = mb->type_; break;
case 2: *info = mb->segment_; break; case 2: *info = mb->segment_; break;
case 3: *info = enc->dqm_[mb->segment_].quant_; break; case 3: *info = enc->dqm_[mb->segment_].quant_; break;
@ -560,6 +762,7 @@ static void ResetAfterSkip(VP8EncIterator* const it) {
int VP8EncLoop(VP8Encoder* const enc) { int VP8EncLoop(VP8Encoder* const enc) {
int i, s, p; int i, s, p;
int ok = 1;
VP8EncIterator it; VP8EncIterator it;
VP8ModeScore info; VP8ModeScore info;
const int dont_use_skip = !enc->proba_.use_skip_proba_; const int dont_use_skip = !enc->proba_.use_skip_proba_;
@ -573,7 +776,7 @@ int VP8EncLoop(VP8Encoder* const enc) {
VP8BitWriterInit(enc->parts_ + p, bytes_per_parts); VP8BitWriterInit(enc->parts_ + p, bytes_per_parts);
} }
ResetStats(enc, rd_opt != 0); ResetStats(enc);
ResetSSE(enc); ResetSSE(enc);
VP8IteratorInit(enc, &it); VP8IteratorInit(enc, &it);
@ -588,9 +791,6 @@ int VP8EncLoop(VP8Encoder* const enc) {
ResetAfterSkip(&it); ResetAfterSkip(&it);
} }
#ifdef WEBP_EXPERIMENTAL_FEATURES #ifdef WEBP_EXPERIMENTAL_FEATURES
if (enc->has_alpha_) {
VP8EncCodeAlphaBlock(&it);
}
if (enc->use_layer_) { if (enc->use_layer_) {
VP8EncCodeLayerBlock(&it); VP8EncCodeLayerBlock(&it);
} }
@ -598,22 +798,31 @@ int VP8EncLoop(VP8Encoder* const enc) {
StoreSideInfo(&it); StoreSideInfo(&it);
VP8StoreFilterStats(&it); VP8StoreFilterStats(&it);
VP8IteratorExport(&it); VP8IteratorExport(&it);
} while (VP8IteratorNext(&it, it.yuv_out_)); ok = VP8IteratorProgress(&it, 20);
VP8AdjustFilterStrength(&it); } while (ok && VP8IteratorNext(&it, it.yuv_out_));
// Finalize the partitions if (ok) { // Finalize the partitions, check for extra errors.
for (p = 0; p < enc->num_parts_; ++p) { for (p = 0; p < enc->num_parts_; ++p) {
VP8BitWriterFinish(enc->parts_ + p); VP8BitWriterFinish(enc->parts_ + p);
} ok &= !enc->parts_[p].error_;
// and byte counters
if (enc->pic_->stats) {
for (i = 0; i <= 2; ++i) {
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
enc->residual_bytes_[i][s] = (int)((it.bit_count_[s][i] + 7) >> 3);
}
} }
} }
return 1;
if (ok) { // All good. Finish up.
if (enc->pic_->stats) { // finalize byte counters...
for (i = 0; i <= 2; ++i) {
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
enc->residual_bytes_[i][s] = (int)((it.bit_count_[s][i] + 7) >> 3);
}
}
}
VP8AdjustFilterStrength(&it); // ...and store filter stats.
} else {
// Something bad happened -> need to do some memory cleanup.
VP8EncFreeBitWriters(enc);
}
return ok;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -625,7 +834,7 @@ int VP8EncLoop(VP8Encoder* const enc) {
#define kHeaderSizeEstimate (15 + 20 + 10) // TODO: fix better #define kHeaderSizeEstimate (15 + 20 + 10) // TODO: fix better
static int OneStatPass(VP8Encoder* const enc, float q, int rd_opt, int nb_mbs, static int OneStatPass(VP8Encoder* const enc, float q, int rd_opt, int nb_mbs,
float* const PSNR) { float* const PSNR, int percent_delta) {
VP8EncIterator it; VP8EncIterator it;
uint64_t size = 0; uint64_t size = 0;
uint64_t distortion = 0; uint64_t distortion = 0;
@ -640,7 +849,7 @@ static int OneStatPass(VP8Encoder* const enc, float q, int rd_opt, int nb_mbs,
VP8SetSegmentParams(enc, q); // setup segment quantizations and filters VP8SetSegmentParams(enc, q); // setup segment quantizations and filters
ResetStats(enc, rd_opt != 0); ResetStats(enc);
ResetTokenStats(enc); ResetTokenStats(enc);
VP8IteratorInit(enc, &it); VP8IteratorInit(enc, &it);
@ -654,6 +863,8 @@ static int OneStatPass(VP8Encoder* const enc, float q, int rd_opt, int nb_mbs,
RecordResiduals(&it, &info); RecordResiduals(&it, &info);
size += info.R; size += info.R;
distortion += info.D; distortion += info.D;
if (percent_delta && !VP8IteratorProgress(&it, percent_delta))
return 0;
} while (VP8IteratorNext(&it, it.yuv_out_) && --nb_mbs > 0); } while (VP8IteratorNext(&it, it.yuv_out_) && --nb_mbs > 0);
size += FinalizeSkipProba(enc); size += FinalizeSkipProba(enc);
size += FinalizeTokenProbas(enc); size += FinalizeTokenProbas(enc);
@ -674,6 +885,10 @@ int VP8StatLoop(VP8Encoder* const enc) {
(enc->config_->target_size > 0 || enc->config_->target_PSNR > 0); (enc->config_->target_size > 0 || enc->config_->target_PSNR > 0);
const int fast_probe = (enc->method_ < 2 && !do_search); const int fast_probe = (enc->method_ < 2 && !do_search);
float q = enc->config_->quality; float q = enc->config_->quality;
const int max_passes = enc->config_->pass;
const int task_percent = 20;
const int percent_per_pass = (task_percent + max_passes / 2) / max_passes;
const int final_percent = enc->percent_ + task_percent;
int pass; int pass;
int nb_mbs; int nb_mbs;
@ -683,36 +898,38 @@ int VP8StatLoop(VP8Encoder* const enc) {
// No target size: just do several pass without changing 'q' // No target size: just do several pass without changing 'q'
if (!do_search) { if (!do_search) {
for (pass = 0; pass < enc->config_->pass; ++pass) { for (pass = 0; pass < max_passes; ++pass) {
const int rd_opt = (enc->method_ > 2); const int rd_opt = (enc->method_ > 2);
OneStatPass(enc, q, rd_opt, nb_mbs, NULL); if (!OneStatPass(enc, q, rd_opt, nb_mbs, NULL, percent_per_pass)) {
return 0;
}
} }
return 1; } else {
} // binary search for a size close to target
for (pass = 0; pass < max_passes && (dqs[pass] > 0); ++pass) {
// binary search for a size close to target const int rd_opt = 1;
for (pass = 0; pass < enc->config_->pass && (dqs[pass] > 0); ++pass) { float PSNR;
const int rd_opt = 1; int criterion;
float PSNR; const int size = OneStatPass(enc, q, rd_opt, nb_mbs, &PSNR,
int criterion; percent_per_pass);
const int size = OneStatPass(enc, q, rd_opt, nb_mbs, &PSNR);
#if DEBUG_SEARCH #if DEBUG_SEARCH
printf("#%d size=%d PSNR=%.2f q=%.2f\n", pass, size, PSNR, q); printf("#%d size=%d PSNR=%.2f q=%.2f\n", pass, size, PSNR, q);
#endif #endif
if (!size) return 0;
if (enc->config_->target_PSNR > 0) { if (enc->config_->target_PSNR > 0) {
criterion = (PSNR < enc->config_->target_PSNR); criterion = (PSNR < enc->config_->target_PSNR);
} else { } else {
criterion = (size < enc->config_->target_size); criterion = (size < enc->config_->target_size);
} }
// dichotomize // dichotomize
if (criterion) { if (criterion) {
q += dqs[pass]; q += dqs[pass];
} else { } else {
q -= dqs[pass]; q -= dqs[pass];
}
} }
} }
return 1; return WebPReportProgress(enc->pic_, final_percent, &enc->percent_);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

406
src/enc/histogram.c Normal file
View File

@ -0,0 +1,406 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Author: Jyrki Alakuijala (jyrki@google.com)
//
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#include <stdio.h>
#include "./backward_references.h"
#include "./histogram.h"
#include "../dsp/lossless.h"
#include "../utils/utils.h"
static void HistogramClear(VP8LHistogram* const p) {
memset(p->literal_, 0, sizeof(p->literal_));
memset(p->red_, 0, sizeof(p->red_));
memset(p->blue_, 0, sizeof(p->blue_));
memset(p->alpha_, 0, sizeof(p->alpha_));
memset(p->distance_, 0, sizeof(p->distance_));
p->bit_cost_ = 0;
}
void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
VP8LHistogram* const histo) {
int i;
for (i = 0; i < refs->size; ++i) {
VP8LHistogramAddSinglePixOrCopy(histo, &refs->refs[i]);
}
}
void VP8LHistogramCreate(VP8LHistogram* const p,
const VP8LBackwardRefs* const refs,
int palette_code_bits) {
if (palette_code_bits >= 0) {
p->palette_code_bits_ = palette_code_bits;
}
HistogramClear(p);
VP8LHistogramStoreRefs(refs, p);
}
void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) {
p->palette_code_bits_ = palette_code_bits;
HistogramClear(p);
}
VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
int i;
VP8LHistogramSet* set;
VP8LHistogram* bulk;
const uint64_t total_size = (uint64_t)sizeof(*set)
+ size * sizeof(*set->histograms)
+ size * sizeof(**set->histograms);
uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory));
if (memory == NULL) return NULL;
set = (VP8LHistogramSet*)memory;
memory += sizeof(*set);
set->histograms = (VP8LHistogram**)memory;
memory += size * sizeof(*set->histograms);
bulk = (VP8LHistogram*)memory;
set->max_size = size;
set->size = size;
for (i = 0; i < size; ++i) {
set->histograms[i] = bulk + i;
VP8LHistogramInit(set->histograms[i], cache_bits);
}
return set;
}
// -----------------------------------------------------------------------------
void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
const PixOrCopy* const v) {
if (PixOrCopyIsLiteral(v)) {
++histo->alpha_[PixOrCopyLiteral(v, 3)];
++histo->red_[PixOrCopyLiteral(v, 2)];
++histo->literal_[PixOrCopyLiteral(v, 1)];
++histo->blue_[PixOrCopyLiteral(v, 0)];
} else if (PixOrCopyIsCacheIdx(v)) {
int literal_ix = 256 + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v);
++histo->literal_[literal_ix];
} else {
int code, extra_bits_count, extra_bits_value;
PrefixEncode(PixOrCopyLength(v),
&code, &extra_bits_count, &extra_bits_value);
++histo->literal_[256 + code];
PrefixEncode(PixOrCopyDistance(v),
&code, &extra_bits_count, &extra_bits_value);
++histo->distance_[code];
}
}
static double BitsEntropy(const int* const array, int n) {
double retval = 0.;
int sum = 0;
int nonzeros = 0;
int max_val = 0;
int i;
double mix;
for (i = 0; i < n; ++i) {
if (array[i] != 0) {
sum += array[i];
++nonzeros;
retval -= VP8LFastSLog2(array[i]);
if (max_val < array[i]) {
max_val = array[i];
}
}
}
retval += VP8LFastSLog2(sum);
if (nonzeros < 5) {
if (nonzeros <= 1) {
return 0;
}
// Two symbols, they will be 0 and 1 in a Huffman code.
// Let's mix in a bit of entropy to favor good clustering when
// distributions of these are combined.
if (nonzeros == 2) {
return 0.99 * sum + 0.01 * retval;
}
// No matter what the entropy says, we cannot be better than min_limit
// with Huffman coding. I am mixing a bit of entropy into the
// min_limit since it produces much better (~0.5 %) compression results
// perhaps because of better entropy clustering.
if (nonzeros == 3) {
mix = 0.95;
} else {
mix = 0.7; // nonzeros == 4.
}
} else {
mix = 0.627;
}
{
double min_limit = 2 * sum - max_val;
min_limit = mix * min_limit + (1.0 - mix) * retval;
return (retval < min_limit) ? min_limit : retval;
}
}
double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) {
double retval = BitsEntropy(&p->literal_[0], VP8LHistogramNumCodes(p))
+ BitsEntropy(&p->red_[0], 256)
+ BitsEntropy(&p->blue_[0], 256)
+ BitsEntropy(&p->alpha_[0], 256)
+ BitsEntropy(&p->distance_[0], NUM_DISTANCE_CODES);
// Compute the extra bits cost.
int i;
for (i = 2; i < NUM_LENGTH_CODES - 2; ++i) {
retval +=
(i >> 1) * p->literal_[256 + i + 2];
}
for (i = 2; i < NUM_DISTANCE_CODES - 2; ++i) {
retval += (i >> 1) * p->distance_[i + 2];
}
return retval;
}
// Returns the cost encode the rle-encoded entropy code.
// The constants in this function are experimental.
static double HuffmanCost(const int* const population, int length) {
// Small bias because Huffman code length is typically not stored in
// full length.
static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3;
static const double kSmallBias = 9.1;
double retval = kHuffmanCodeOfHuffmanCodeSize - kSmallBias;
int streak = 0;
int i = 0;
for (; i < length - 1; ++i) {
++streak;
if (population[i] == population[i + 1]) {
continue;
}
last_streak_hack:
// population[i] points now to the symbol in the streak of same values.
if (streak > 3) {
if (population[i] == 0) {
retval += 1.5625 + 0.234375 * streak;
} else {
retval += 2.578125 + 0.703125 * streak;
}
} else {
if (population[i] == 0) {
retval += 1.796875 * streak;
} else {
retval += 3.28125 * streak;
}
}
streak = 0;
}
if (i == length - 1) {
++streak;
goto last_streak_hack;
}
return retval;
}
// Estimates the Huffman dictionary + other block overhead size.
static double HistogramEstimateBitsHeader(const VP8LHistogram* const p) {
return HuffmanCost(&p->alpha_[0], 256) +
HuffmanCost(&p->red_[0], 256) +
HuffmanCost(&p->literal_[0], VP8LHistogramNumCodes(p)) +
HuffmanCost(&p->blue_[0], 256) +
HuffmanCost(&p->distance_[0], NUM_DISTANCE_CODES);
}
double VP8LHistogramEstimateBits(const VP8LHistogram* const p) {
return HistogramEstimateBitsHeader(p) + VP8LHistogramEstimateBitsBulk(p);
}
static void HistogramBuildImage(int xsize, int histo_bits,
const VP8LBackwardRefs* const backward_refs,
VP8LHistogramSet* const image) {
int i;
int x = 0, y = 0;
const int histo_xsize = VP8LSubSampleSize(xsize, histo_bits);
VP8LHistogram** const histograms = image->histograms;
assert(histo_bits > 0);
for (i = 0; i < backward_refs->size; ++i) {
const PixOrCopy* const v = &backward_refs->refs[i];
const int ix = (y >> histo_bits) * histo_xsize + (x >> histo_bits);
VP8LHistogramAddSinglePixOrCopy(histograms[ix], v);
x += PixOrCopyLength(v);
while (x >= xsize) {
x -= xsize;
++y;
}
}
}
static uint32_t MyRand(uint32_t *seed) {
*seed *= 16807U;
if (*seed == 0) {
*seed = 1;
}
return *seed;
}
static int HistogramCombine(const VP8LHistogramSet* const in,
VP8LHistogramSet* const out, int num_pairs) {
int ok = 0;
int i, iter;
uint32_t seed = 0;
int tries_with_no_success = 0;
const int min_cluster_size = 2;
int out_size = in->size;
const int outer_iters = in->size * 3;
VP8LHistogram* const histos = (VP8LHistogram*)malloc(2 * sizeof(*histos));
VP8LHistogram* cur_combo = histos + 0; // trial merged histogram
VP8LHistogram* best_combo = histos + 1; // best merged histogram so far
if (histos == NULL) goto End;
// Copy histograms from in[] to out[].
assert(in->size <= out->size);
for (i = 0; i < in->size; ++i) {
in->histograms[i]->bit_cost_ = VP8LHistogramEstimateBits(in->histograms[i]);
*out->histograms[i] = *in->histograms[i];
}
// Collapse similar histograms in 'out'.
for (iter = 0; iter < outer_iters && out_size >= min_cluster_size; ++iter) {
// We pick the best pair to be combined out of 'inner_iters' pairs.
double best_cost_diff = 0.;
int best_idx1 = 0, best_idx2 = 1;
int j;
seed += iter;
for (j = 0; j < num_pairs; ++j) {
double curr_cost_diff;
// Choose two histograms at random and try to combine them.
const uint32_t idx1 = MyRand(&seed) % out_size;
const uint32_t tmp = ((j & 7) + 1) % (out_size - 1);
const uint32_t diff = (tmp < 3) ? tmp : MyRand(&seed) % (out_size - 1);
const uint32_t idx2 = (idx1 + diff + 1) % out_size;
if (idx1 == idx2) {
continue;
}
*cur_combo = *out->histograms[idx1];
VP8LHistogramAdd(cur_combo, out->histograms[idx2]);
cur_combo->bit_cost_ = VP8LHistogramEstimateBits(cur_combo);
// Calculate cost reduction on combining.
curr_cost_diff = cur_combo->bit_cost_
- out->histograms[idx1]->bit_cost_
- out->histograms[idx2]->bit_cost_;
if (best_cost_diff > curr_cost_diff) { // found a better pair?
{ // swap cur/best combo histograms
VP8LHistogram* const tmp_histo = cur_combo;
cur_combo = best_combo;
best_combo = tmp_histo;
}
best_cost_diff = curr_cost_diff;
best_idx1 = idx1;
best_idx2 = idx2;
}
}
if (best_cost_diff < 0.0) {
*out->histograms[best_idx1] = *best_combo;
// swap best_idx2 slot with last one (which is now unused)
--out_size;
if (best_idx2 != out_size) {
out->histograms[best_idx2] = out->histograms[out_size];
out->histograms[out_size] = NULL; // just for sanity check.
}
tries_with_no_success = 0;
}
if (++tries_with_no_success >= 50) {
break;
}
}
out->size = out_size;
ok = 1;
End:
free(histos);
return ok;
}
// -----------------------------------------------------------------------------
// Histogram refinement
// What is the bit cost of moving square_histogram from
// cur_symbol to candidate_symbol.
// TODO(skal): we don't really need to copy the histogram and Add(). Instead
// we just need VP8LDualHistogramEstimateBits(A, B) estimation function.
static double HistogramDistance(const VP8LHistogram* const square_histogram,
const VP8LHistogram* const candidate) {
const double previous_bit_cost = candidate->bit_cost_;
double new_bit_cost;
VP8LHistogram modified_histo;
modified_histo = *candidate;
VP8LHistogramAdd(&modified_histo, square_histogram);
new_bit_cost = VP8LHistogramEstimateBits(&modified_histo);
return new_bit_cost - previous_bit_cost;
}
// Find the best 'out' histogram for each of the 'in' histograms.
// Note: we assume that out[]->bit_cost_ is already up-to-date.
static void HistogramRemap(const VP8LHistogramSet* const in,
const VP8LHistogramSet* const out,
uint16_t* const symbols) {
int i;
for (i = 0; i < in->size; ++i) {
int best_out = 0;
double best_bits = HistogramDistance(in->histograms[i], out->histograms[0]);
int k;
for (k = 1; k < out->size; ++k) {
const double cur_bits =
HistogramDistance(in->histograms[i], out->histograms[k]);
if (cur_bits < best_bits) {
best_bits = cur_bits;
best_out = k;
}
}
symbols[i] = best_out;
}
// Recompute each out based on raw and symbols.
for (i = 0; i < out->size; ++i) {
HistogramClear(out->histograms[i]);
}
for (i = 0; i < in->size; ++i) {
VP8LHistogramAdd(out->histograms[symbols[i]], in->histograms[i]);
}
}
int VP8LGetHistoImageSymbols(int xsize, int ysize,
const VP8LBackwardRefs* const refs,
int quality, int histo_bits, int cache_bits,
VP8LHistogramSet* const image_in,
uint16_t* const histogram_symbols) {
int ok = 0;
const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1;
const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1;
const int num_histo_pairs = 10 + quality / 2; // For HistogramCombine().
const int histo_image_raw_size = histo_xsize * histo_ysize;
VP8LHistogramSet* const image_out =
VP8LAllocateHistogramSet(histo_image_raw_size, cache_bits);
if (image_out == NULL) return 0;
// Build histogram image.
HistogramBuildImage(xsize, histo_bits, refs, image_out);
// Collapse similar histograms.
if (!HistogramCombine(image_out, image_in, num_histo_pairs)) {
goto Error;
}
// Find the optimal map from original histograms to the final ones.
HistogramRemap(image_out, image_in, histogram_symbols);
ok = 1;
Error:
free(image_out);
return ok;
}

115
src/enc/histogram.h Normal file
View File

@ -0,0 +1,115 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Author: Jyrki Alakuijala (jyrki@google.com)
//
// Models the histograms of literal and distance codes.
#ifndef WEBP_ENC_HISTOGRAM_H_
#define WEBP_ENC_HISTOGRAM_H_
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "./backward_references.h"
#include "../webp/format_constants.h"
#include "../webp/types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
// A simple container for histograms of data.
typedef struct {
// literal_ contains green literal, palette-code and
// copy-length-prefix histogram
int literal_[PIX_OR_COPY_CODES_MAX];
int red_[256];
int blue_[256];
int alpha_[256];
// Backward reference prefix-code histogram.
int distance_[NUM_DISTANCE_CODES];
int palette_code_bits_;
double bit_cost_; // cached value of VP8LHistogramEstimateBits(this)
} VP8LHistogram;
// Collection of histograms with fixed capacity, allocated as one
// big memory chunk. Can be destroyed by simply calling 'free()'.
typedef struct {
int size; // number of slots currently in use
int max_size; // maximum capacity
VP8LHistogram** histograms;
} VP8LHistogramSet;
// Create the histogram.
//
// The input data is the PixOrCopy data, which models the literals, stop
// codes and backward references (both distances and lengths). Also: if
// palette_code_bits is >= 0, initialize the histogram with this value.
void VP8LHistogramCreate(VP8LHistogram* const p,
const VP8LBackwardRefs* const refs,
int palette_code_bits);
// Set the palette_code_bits and reset the stats.
void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits);
// Collect all the references into a histogram (without reset)
void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
VP8LHistogram* const histo);
// Allocate an array of pointer to histograms, allocated and initialized
// using 'cache_bits'. Return NULL in case of memory error.
VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits);
// Accumulate a token 'v' into a histogram.
void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
const PixOrCopy* const v);
// Estimate how many bits the combined entropy of literals and distance
// approximately maps to.
double VP8LHistogramEstimateBits(const VP8LHistogram* const p);
// This function estimates the cost in bits excluding the bits needed to
// represent the entropy code itself.
double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p);
static WEBP_INLINE void VP8LHistogramAdd(VP8LHistogram* const p,
const VP8LHistogram* const a) {
int i;
for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) {
p->literal_[i] += a->literal_[i];
}
for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
p->distance_[i] += a->distance_[i];
}
for (i = 0; i < 256; ++i) {
p->red_[i] += a->red_[i];
p->blue_[i] += a->blue_[i];
p->alpha_[i] += a->alpha_[i];
}
}
static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) {
return 256 + NUM_LENGTH_CODES +
((p->palette_code_bits_ > 0) ? (1 << p->palette_code_bits_) : 0);
}
// Builds the histogram image.
int VP8LGetHistoImageSymbols(int xsize, int ysize,
const VP8LBackwardRefs* const refs,
int quality, int histogram_bits, int cache_bits,
VP8LHistogramSet* const image_in,
uint16_t* const histogram_symbols);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif // WEBP_ENC_HISTOGRAM_H_

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,9 +9,9 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "vp8enci.h"
#include "./vp8enci.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -24,7 +24,7 @@ extern "C" {
static void InitLeft(VP8EncIterator* const it) { static void InitLeft(VP8EncIterator* const it) {
const VP8Encoder* const enc = it->enc_; const VP8Encoder* const enc = it->enc_;
enc->y_left_[-1] = enc->u_left_[-1] = enc->v_left_[-1] = enc->y_left_[-1] = enc->u_left_[-1] = enc->v_left_[-1] =
(it->y_) > 0 ? 129 : 127; (it->y_ > 0) ? 129 : 127;
memset(enc->y_left_, 129, 16); memset(enc->y_left_, 129, 16);
memset(enc->u_left_, 129, 8); memset(enc->u_left_, 129, 8);
memset(enc->v_left_, 129, 8); memset(enc->v_left_, 129, 8);
@ -33,7 +33,7 @@ static void InitLeft(VP8EncIterator* const it) {
static void InitTop(VP8EncIterator* const it) { static void InitTop(VP8EncIterator* const it) {
const VP8Encoder* const enc = it->enc_; const VP8Encoder* const enc = it->enc_;
const int top_size = enc->mb_w_ * 16; const size_t top_size = enc->mb_w_ * 16;
memset(enc->y_top_, 127, 2 * top_size); memset(enc->y_top_, 127, 2 * top_size);
memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_)); memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_));
} }
@ -65,66 +65,81 @@ void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) {
it->yuv_out2_ = enc->yuv_out2_; it->yuv_out2_ = enc->yuv_out2_;
it->yuv_p_ = enc->yuv_p_; it->yuv_p_ = enc->yuv_p_;
it->lf_stats_ = enc->lf_stats_; it->lf_stats_ = enc->lf_stats_;
it->percent0_ = enc->percent_;
VP8IteratorReset(it); VP8IteratorReset(it);
} }
int VP8IteratorProgress(const VP8EncIterator* const it, int delta) {
VP8Encoder* const enc = it->enc_;
if (delta && enc->pic_->progress_hook) {
const int percent = (enc->mb_h_ <= 1)
? it->percent0_
: it->percent0_ + delta * it->y_ / (enc->mb_h_ - 1);
return WebPReportProgress(enc->pic_, percent, &enc->percent_);
}
return 1;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Import the source samples into the cache. Takes care of replicating // Import the source samples into the cache. Takes care of replicating
// boundary pixels if necessary. // boundary pixels if necessary.
static void ImportBlock(const uint8_t* src, int src_stride,
uint8_t* dst, int w, int h, int size) {
int i;
for (i = 0; i < h; ++i) {
memcpy(dst, src, w);
if (w < size) {
memset(dst + w, dst[w - 1], size - w);
}
dst += BPS;
src += src_stride;
}
for (i = h; i < size; ++i) {
memcpy(dst, dst - BPS, size);
dst += BPS;
}
}
void VP8IteratorImport(const VP8EncIterator* const it) { void VP8IteratorImport(const VP8EncIterator* const it) {
const VP8Encoder* const enc = it->enc_; const VP8Encoder* const enc = it->enc_;
const int x = it->x_, y = it->y_; const int x = it->x_, y = it->y_;
const WebPPicture* const pic = enc->pic_; const WebPPicture* const pic = enc->pic_;
const uint8_t* ysrc = pic->y + (y * pic->y_stride + x) * 16; const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16;
const uint8_t* usrc = pic->u + (y * pic->uv_stride + x) * 8; const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8;
const uint8_t* vsrc = pic->v + (y * pic->uv_stride + x) * 8; const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8;
uint8_t* ydst = it->yuv_in_ + Y_OFF; uint8_t* const ydst = it->yuv_in_ + Y_OFF;
uint8_t* udst = it->yuv_in_ + U_OFF; uint8_t* const udst = it->yuv_in_ + U_OFF;
uint8_t* vdst = it->yuv_in_ + V_OFF; uint8_t* const vdst = it->yuv_in_ + V_OFF;
int w = (pic->width - x * 16); int w = (pic->width - x * 16);
int h = (pic->height - y * 16); int h = (pic->height - y * 16);
int i;
if (w > 16) w = 16; if (w > 16) w = 16;
if (h > 16) h = 16; if (h > 16) h = 16;
// Luma plane // Luma plane
for (i = 0; i < h; ++i) { ImportBlock(ysrc, pic->y_stride, ydst, w, h, 16);
memcpy(ydst, ysrc, w);
if (w < 16) memset(ydst + w, ydst[w - 1], 16 - w); { // U/V planes
ydst += BPS; const int uv_w = (w + 1) >> 1;
ysrc += pic->y_stride; const int uv_h = (h + 1) >> 1;
} ImportBlock(usrc, pic->uv_stride, udst, uv_w, uv_h, 8);
for (i = h; i < 16; ++i) { ImportBlock(vsrc, pic->uv_stride, vdst, uv_w, uv_h, 8);
memcpy(ydst, ydst - BPS, 16);
ydst += BPS;
}
// U/V plane
w = (w + 1) / 2;
h = (h + 1) / 2;
for (i = 0; i < h; ++i) {
memcpy(udst, usrc, w);
memcpy(vdst, vsrc, w);
if (w < 8) {
memset(udst + w, udst[w - 1], 8 - w);
memset(vdst + w, vdst[w - 1], 8 - w);
}
udst += BPS;
vdst += BPS;
usrc += pic->uv_stride;
vsrc += pic->uv_stride;
}
for (i = h; i < 8; ++i) {
memcpy(udst, udst - BPS, 8);
memcpy(vdst, vdst - BPS, 8);
udst += BPS;
vdst += BPS;
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Copy back the compressed samples into user space if requested. // Copy back the compressed samples into user space if requested.
static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride,
int w, int h) {
while (h-- > 0) {
memcpy(dst, src, w);
dst += dst_stride;
src += BPS;
}
}
void VP8IteratorExport(const VP8EncIterator* const it) { void VP8IteratorExport(const VP8EncIterator* const it) {
const VP8Encoder* const enc = it->enc_; const VP8Encoder* const enc = it->enc_;
if (enc->config_->show_compressed) { if (enc->config_->show_compressed) {
@ -133,28 +148,23 @@ void VP8IteratorExport(const VP8EncIterator* const it) {
const uint8_t* const usrc = it->yuv_out_ + U_OFF; const uint8_t* const usrc = it->yuv_out_ + U_OFF;
const uint8_t* const vsrc = it->yuv_out_ + V_OFF; const uint8_t* const vsrc = it->yuv_out_ + V_OFF;
const WebPPicture* const pic = enc->pic_; const WebPPicture* const pic = enc->pic_;
uint8_t* ydst = pic->y + (y * pic->y_stride + x) * 16; uint8_t* const ydst = pic->y + (y * pic->y_stride + x) * 16;
uint8_t* udst = pic->u + (y * pic->uv_stride + x) * 8; uint8_t* const udst = pic->u + (y * pic->uv_stride + x) * 8;
uint8_t* vdst = pic->v + (y * pic->uv_stride + x) * 8; uint8_t* const vdst = pic->v + (y * pic->uv_stride + x) * 8;
int w = (pic->width - x * 16); int w = (pic->width - x * 16);
int h = (pic->height - y * 16); int h = (pic->height - y * 16);
int i;
if (w > 16) w = 16; if (w > 16) w = 16;
if (h > 16) h = 16; if (h > 16) h = 16;
// Luma plane // Luma plane
for (i = 0; i < h; ++i) { ExportBlock(ysrc, ydst, pic->y_stride, w, h);
memcpy(ydst + i * pic->y_stride, ysrc + i * BPS, w);
} { // U/V planes
// U/V plane const int uv_w = (w + 1) >> 1;
{ const int uv_h = (h + 1) >> 1;
const int uv_w = (w + 1) / 2; ExportBlock(usrc, udst, pic->uv_stride, uv_w, uv_h);
const int uv_h = (h + 1) / 2; ExportBlock(vsrc, vdst, pic->uv_stride, uv_w, uv_h);
for (i = 0; i < uv_h; ++i) {
memcpy(udst + i * pic->uv_stride, usrc + i * BPS, uv_w);
memcpy(vdst + i * pic->uv_stride, vsrc + i * BPS, uv_w);
}
} }
} }
} }
@ -178,47 +188,51 @@ void VP8IteratorExport(const VP8EncIterator* const it) {
void VP8IteratorNzToBytes(VP8EncIterator* const it) { void VP8IteratorNzToBytes(VP8EncIterator* const it) {
const int tnz = it->nz_[0], lnz = it->nz_[-1]; const int tnz = it->nz_[0], lnz = it->nz_[-1];
int* const top_nz = it->top_nz_;
int* const left_nz = it->left_nz_;
// Top-Y // Top-Y
it->top_nz_[0] = BIT(tnz, 12); top_nz[0] = BIT(tnz, 12);
it->top_nz_[1] = BIT(tnz, 13); top_nz[1] = BIT(tnz, 13);
it->top_nz_[2] = BIT(tnz, 14); top_nz[2] = BIT(tnz, 14);
it->top_nz_[3] = BIT(tnz, 15); top_nz[3] = BIT(tnz, 15);
// Top-U // Top-U
it->top_nz_[4] = BIT(tnz, 18); top_nz[4] = BIT(tnz, 18);
it->top_nz_[5] = BIT(tnz, 19); top_nz[5] = BIT(tnz, 19);
// Top-V // Top-V
it->top_nz_[6] = BIT(tnz, 22); top_nz[6] = BIT(tnz, 22);
it->top_nz_[7] = BIT(tnz, 23); top_nz[7] = BIT(tnz, 23);
// DC // DC
it->top_nz_[8] = BIT(tnz, 24); top_nz[8] = BIT(tnz, 24);
// left-Y // left-Y
it->left_nz_[0] = BIT(lnz, 3); left_nz[0] = BIT(lnz, 3);
it->left_nz_[1] = BIT(lnz, 7); left_nz[1] = BIT(lnz, 7);
it->left_nz_[2] = BIT(lnz, 11); left_nz[2] = BIT(lnz, 11);
it->left_nz_[3] = BIT(lnz, 15); left_nz[3] = BIT(lnz, 15);
// left-U // left-U
it->left_nz_[4] = BIT(lnz, 17); left_nz[4] = BIT(lnz, 17);
it->left_nz_[5] = BIT(lnz, 19); left_nz[5] = BIT(lnz, 19);
// left-V // left-V
it->left_nz_[6] = BIT(lnz, 21); left_nz[6] = BIT(lnz, 21);
it->left_nz_[7] = BIT(lnz, 23); left_nz[7] = BIT(lnz, 23);
// left-DC is special, iterated separately // left-DC is special, iterated separately
} }
void VP8IteratorBytesToNz(VP8EncIterator* const it) { void VP8IteratorBytesToNz(VP8EncIterator* const it) {
uint32_t nz = 0; uint32_t nz = 0;
const int* const top_nz = it->top_nz_;
const int* const left_nz = it->left_nz_;
// top // top
nz |= (it->top_nz_[0] << 12) | (it->top_nz_[1] << 13); nz |= (top_nz[0] << 12) | (top_nz[1] << 13);
nz |= (it->top_nz_[2] << 14) | (it->top_nz_[3] << 15); nz |= (top_nz[2] << 14) | (top_nz[3] << 15);
nz |= (it->top_nz_[4] << 18) | (it->top_nz_[5] << 19); nz |= (top_nz[4] << 18) | (top_nz[5] << 19);
nz |= (it->top_nz_[6] << 22) | (it->top_nz_[7] << 23); nz |= (top_nz[6] << 22) | (top_nz[7] << 23);
nz |= (it->top_nz_[8] << 24); // we propagate the _top_ bit, esp. for intra4 nz |= (top_nz[8] << 24); // we propagate the _top_ bit, esp. for intra4
// left // left
nz |= (it->left_nz_[0] << 3) | (it->left_nz_[1] << 7); nz |= (left_nz[0] << 3) | (left_nz[1] << 7);
nz |= (it->left_nz_[2] << 11); nz |= (left_nz[2] << 11);
nz |= (it->left_nz_[4] << 17) | (it->left_nz_[6] << 21); nz |= (left_nz[4] << 17) | (left_nz[6] << 21);
*it->nz_ = nz; *it->nz_ = nz;
} }
@ -274,8 +288,8 @@ int VP8IteratorNext(VP8EncIterator* const it,
// Helper function to set mode properties // Helper function to set mode properties
void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) { void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) {
int y;
uint8_t* preds = it->preds_; uint8_t* preds = it->preds_;
int y;
for (y = 0; y < 4; ++y) { for (y = 0; y < 4; ++y) {
memset(preds, mode, 4); memset(preds, mode, 4);
preds += it->enc_->preds_w_; preds += it->enc_->preds_w_;
@ -283,14 +297,13 @@ void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) {
it->mb_->type_ = 1; it->mb_->type_ = 1;
} }
void VP8SetIntra4Mode(const VP8EncIterator* const it, int modes[16]) { void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes) {
int x, y;
uint8_t* preds = it->preds_; uint8_t* preds = it->preds_;
for (y = 0; y < 4; ++y) { int y;
for (x = 0; x < 4; ++x) { for (y = 4; y > 0; --y) {
preds[x] = modes[x + y * 4]; memcpy(preds, modes, 4 * sizeof(*modes));
}
preds += it->enc_->preds_w_; preds += it->enc_->preds_w_;
modes += 4;
} }
it->mb_->type_ = 0; it->mb_->type_ = 0;
} }
@ -347,7 +360,7 @@ static const uint8_t VP8TopLeftI4[16] = {
}; };
void VP8IteratorStartI4(VP8EncIterator* const it) { void VP8IteratorStartI4(VP8EncIterator* const it) {
VP8Encoder* const enc = it->enc_; const VP8Encoder* const enc = it->enc_;
int i; int i;
it->i4_ = 0; // first 4x4 sub-block it->i4_ = 0; // first 4x4 sub-block
@ -393,7 +406,7 @@ int VP8IteratorRotateI4(VP8EncIterator* const it,
} }
} }
// move pointers to next sub-block // move pointers to next sub-block
it->i4_++; ++it->i4_;
if (it->i4_ == 16) { // we're done if (it->i4_ == 16) { // we're done
return 0; return 0;
} }

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,18 +9,14 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include "vp8enci.h"
#include "./vp8enci.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
#ifdef WEBP_EXPERIMENTAL_FEATURES
#endif /* WEBP_EXPERIMENTAL_FEATURES */
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void VP8EncInitLayer(VP8Encoder* const enc) { void VP8EncInitLayer(VP8Encoder* const enc) {
@ -34,8 +30,6 @@ void VP8EncInitLayer(VP8Encoder* const enc) {
void VP8EncCodeLayerBlock(VP8EncIterator* it) { void VP8EncCodeLayerBlock(VP8EncIterator* it) {
(void)it; // remove a warning (void)it; // remove a warning
#ifdef WEBP_EXPERIMENTAL_FEATURES
#endif /* WEBP_EXPERIMENTAL_FEATURES */
} }
int VP8EncFinishLayer(VP8Encoder* const enc) { int VP8EncFinishLayer(VP8Encoder* const enc) {

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -12,8 +12,8 @@
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include "vp8enci.h" #include "./vp8enci.h"
#include "cost.h" #include "./cost.h"
#define DO_TRELLIS_I4 1 #define DO_TRELLIS_I4 1
#define DO_TRELLIS_I16 1 // not a huge gain, but ok at low bitrate. #define DO_TRELLIS_I16 1 // not a huge gain, but ok at low bitrate.
@ -35,7 +35,7 @@ extern "C" {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static inline int clip(int v, int m, int M) { static WEBP_INLINE int clip(int v, int m, int M) {
return v < m ? m : v > M ? M : v; return v < m ? m : v > M ? M : v;
} }
@ -299,16 +299,16 @@ const int VP8I4ModeOffsets[NUM_BMODES] = {
}; };
void VP8MakeLuma16Preds(const VP8EncIterator* const it) { void VP8MakeLuma16Preds(const VP8EncIterator* const it) {
VP8Encoder* const enc = it->enc_; const VP8Encoder* const enc = it->enc_;
const uint8_t* left = it->x_ ? enc->y_left_ : NULL; const uint8_t* const left = it->x_ ? enc->y_left_ : NULL;
const uint8_t* top = it->y_ ? enc->y_top_ + it->x_ * 16 : NULL; const uint8_t* const top = it->y_ ? enc->y_top_ + it->x_ * 16 : NULL;
VP8EncPredLuma16(it->yuv_p_, left, top); VP8EncPredLuma16(it->yuv_p_, left, top);
} }
void VP8MakeChroma8Preds(const VP8EncIterator* const it) { void VP8MakeChroma8Preds(const VP8EncIterator* const it) {
VP8Encoder* const enc = it->enc_; const VP8Encoder* const enc = it->enc_;
const uint8_t* left = it->x_ ? enc->u_left_ : NULL; const uint8_t* const left = it->x_ ? enc->u_left_ : NULL;
const uint8_t* top = it->y_ ? enc->uv_top_ + it->x_ * 16 : NULL; const uint8_t* const top = it->y_ ? enc->uv_top_ + it->x_ * 16 : NULL;
VP8EncPredChroma8(it->yuv_p_, left, top); VP8EncPredChroma8(it->yuv_p_, left, top);
} }
@ -406,13 +406,13 @@ typedef struct {
#define NUM_NODES (MIN_DELTA + 1 + MAX_DELTA) #define NUM_NODES (MIN_DELTA + 1 + MAX_DELTA)
#define NODE(n, l) (nodes[(n) + 1][(l) + MIN_DELTA]) #define NODE(n, l) (nodes[(n) + 1][(l) + MIN_DELTA])
static inline void SetRDScore(int lambda, VP8ModeScore* const rd) { static WEBP_INLINE void SetRDScore(int lambda, VP8ModeScore* const rd) {
// TODO: incorporate the "* 256" in the tables? // TODO: incorporate the "* 256" in the tables?
rd->score = rd->R * lambda + 256 * (rd->D + rd->SD); rd->score = rd->R * lambda + 256 * (rd->D + rd->SD);
} }
static inline score_t RDScoreTrellis(int lambda, score_t rate, static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate,
score_t distortion) { score_t distortion) {
return rate * lambda + 256 * distortion; return rate * lambda + 256 * distortion;
} }
@ -700,7 +700,7 @@ static void SwapOut(VP8EncIterator* const it) {
} }
static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) { static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) {
VP8Encoder* const enc = it->enc_; const VP8Encoder* const enc = it->enc_;
const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
const int lambda = dqm->lambda_i16_; const int lambda = dqm->lambda_i16_;
const int tlambda = dqm->tlambda_; const int tlambda = dqm->tlambda_;
@ -742,7 +742,7 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) {
// return the cost array corresponding to the surrounding prediction modes. // return the cost array corresponding to the surrounding prediction modes.
static const uint16_t* GetCostModeI4(VP8EncIterator* const it, static const uint16_t* GetCostModeI4(VP8EncIterator* const it,
const int modes[16]) { const uint8_t modes[16]) {
const int preds_w = it->enc_->preds_w_; const int preds_w = it->enc_->preds_w_;
const int x = (it->i4_ & 3), y = it->i4_ >> 2; const int x = (it->i4_ & 3), y = it->i4_ >> 2;
const int left = (x == 0) ? it->preds_[y * preds_w - 1] : modes[it->i4_ - 1]; const int left = (x == 0) ? it->preds_[y * preds_w - 1] : modes[it->i4_ - 1];
@ -751,7 +751,7 @@ static const uint16_t* GetCostModeI4(VP8EncIterator* const it,
} }
static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) { static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) {
VP8Encoder* const enc = it->enc_; const VP8Encoder* const enc = it->enc_;
const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
const int lambda = dqm->lambda_i4_; const int lambda = dqm->lambda_i4_;
const int tlambda = dqm->tlambda_; const int tlambda = dqm->tlambda_;
@ -827,7 +827,7 @@ static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) { static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
VP8Encoder* const enc = it->enc_; const VP8Encoder* const enc = it->enc_;
const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
const int lambda = dqm->lambda_uv_; const int lambda = dqm->lambda_uv_;
const uint8_t* const src = it->yuv_in_ + U_OFF; const uint8_t* const src = it->yuv_in_ + U_OFF;

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -10,74 +10,190 @@
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <assert.h> #include <assert.h>
#include <math.h>
#include "vp8enci.h" #include "../webp/format_constants.h"
#include "./vp8enci.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
#define KSIGNATURE 0x9d012a //------------------------------------------------------------------------------
#define KHEADER_SIZE 10 // Helper functions
#define KRIFF_SIZE 20
#define KSIZE_OFFSET (KRIFF_SIZE - 8)
#define MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition // TODO(later): Move to webp/format_constants.h?
#define MAX_PARTITION_SIZE (1 << 24) // max size for token partition static void PutLE24(uint8_t* const data, uint32_t val) {
data[0] = (val >> 0) & 0xff;
data[1] = (val >> 8) & 0xff;
data[2] = (val >> 16) & 0xff;
}
static void PutLE32(uint8_t* const data, uint32_t val) {
PutLE24(data, val);
data[3] = (val >> 24) & 0xff;
}
static int IsVP8XNeeded(const VP8Encoder* const enc) {
return !!enc->has_alpha_; // Currently the only case when VP8X is needed.
// This could change in the future.
}
static int PutPaddingByte(const WebPPicture* const pic) {
const uint8_t pad_byte[1] = { 0 };
return !!pic->writer(pad_byte, 1, pic);
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Writers for header's various pieces (in order of appearance) // Writers for header's various pieces (in order of appearance)
// Main keyframe header static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc,
size_t riff_size) {
static void PutLE32(uint8_t* const data, uint32_t val) { const WebPPicture* const pic = enc->pic_;
data[0] = (val >> 0) & 0xff; uint8_t riff[RIFF_HEADER_SIZE] = {
data[1] = (val >> 8) & 0xff; 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P'
data[2] = (val >> 16) & 0xff; };
data[3] = (val >> 24) & 0xff; assert(riff_size == (uint32_t)riff_size);
PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
if (!pic->writer(riff, sizeof(riff), pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
return VP8_ENC_OK;
} }
static int PutHeader(int profile, size_t size0, size_t total_size, static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) {
WebPPicture* const pic) { const WebPPicture* const pic = enc->pic_;
uint8_t buf[KHEADER_SIZE]; uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = {
uint8_t RIFF[KRIFF_SIZE] = { 'V', 'P', '8', 'X'
'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', 'V', 'P', '8', ' '
}; };
uint32_t flags = 0;
assert(IsVP8XNeeded(enc));
assert(pic->width >= 1 && pic->height >= 1);
assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE);
if (enc->has_alpha_) {
flags |= ALPHA_FLAG_BIT;
}
PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE);
PutLE32(vp8x + CHUNK_HEADER_SIZE, flags);
PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1);
PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1);
if(!pic->writer(vp8x, sizeof(vp8x), pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
return VP8_ENC_OK;
}
static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) {
const WebPPicture* const pic = enc->pic_;
uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = {
'A', 'L', 'P', 'H'
};
assert(enc->has_alpha_);
// Alpha chunk header.
PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size_);
if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
// Alpha chunk data.
if (!pic->writer(enc->alpha_data_, enc->alpha_data_size_, pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
// Padding.
if ((enc->alpha_data_size_ & 1) && !PutPaddingByte(pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
return VP8_ENC_OK;
}
static WebPEncodingError PutVP8Header(const WebPPicture* const pic,
size_t vp8_size) {
uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = {
'V', 'P', '8', ' '
};
assert(vp8_size == (uint32_t)vp8_size);
PutLE32(vp8_chunk_hdr + TAG_SIZE, (uint32_t)vp8_size);
if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
return VP8_ENC_OK;
}
static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic,
int profile, size_t size0) {
uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE];
uint32_t bits; uint32_t bits;
if (size0 >= MAX_PARTITION0_SIZE) { // partition #0 is too big to fit if (size0 >= VP8_MAX_PARTITION0_SIZE) { // partition #0 is too big to fit
return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION0_OVERFLOW); return VP8_ENC_ERROR_PARTITION0_OVERFLOW;
}
if (total_size > 0xfffffffeU - KRIFF_SIZE) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG);
}
PutLE32(RIFF + 4, (uint32_t)(total_size + KSIZE_OFFSET));
PutLE32(RIFF + 16, (uint32_t)total_size);
if (!pic->writer(RIFF, sizeof(RIFF), pic)) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
} }
// Paragraph 9.1.
bits = 0 // keyframe (1b) bits = 0 // keyframe (1b)
| (profile << 1) // profile (3b) | (profile << 1) // profile (3b)
| (1 << 4) // visible (1b) | (1 << 4) // visible (1b)
| ((uint32_t)size0 << 5); // partition length (19b) | ((uint32_t)size0 << 5); // partition length (19b)
buf[0] = bits & 0xff; vp8_frm_hdr[0] = (bits >> 0) & 0xff;
buf[1] = (bits >> 8) & 0xff; vp8_frm_hdr[1] = (bits >> 8) & 0xff;
buf[2] = (bits >> 16) & 0xff; vp8_frm_hdr[2] = (bits >> 16) & 0xff;
// signature // signature
buf[3] = (KSIGNATURE >> 16) & 0xff; vp8_frm_hdr[3] = (VP8_SIGNATURE >> 16) & 0xff;
buf[4] = (KSIGNATURE >> 8) & 0xff; vp8_frm_hdr[4] = (VP8_SIGNATURE >> 8) & 0xff;
buf[5] = (KSIGNATURE >> 0) & 0xff; vp8_frm_hdr[5] = (VP8_SIGNATURE >> 0) & 0xff;
// dimensions // dimensions
buf[6] = pic->width & 0xff; vp8_frm_hdr[6] = pic->width & 0xff;
buf[7] = pic->width >> 8; vp8_frm_hdr[7] = pic->width >> 8;
buf[8] = pic->height & 0xff; vp8_frm_hdr[8] = pic->height & 0xff;
buf[9] = pic->height >> 8; vp8_frm_hdr[9] = pic->height >> 8;
return pic->writer(buf, sizeof(buf), pic); if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
return VP8_ENC_OK;
}
// WebP Headers.
static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0,
size_t vp8_size, size_t riff_size) {
WebPPicture* const pic = enc->pic_;
WebPEncodingError err = VP8_ENC_OK;
// RIFF header.
err = PutRIFFHeader(enc, riff_size);
if (err != VP8_ENC_OK) goto Error;
// VP8X.
if (IsVP8XNeeded(enc)) {
err = PutVP8XHeader(enc);
if (err != VP8_ENC_OK) goto Error;
}
// Alpha.
if (enc->has_alpha_) {
err = PutAlphaChunk(enc);
if (err != VP8_ENC_OK) goto Error;
}
// VP8 header.
err = PutVP8Header(pic, vp8_size);
if (err != VP8_ENC_OK) goto Error;
// VP8 frame header.
err = PutVP8FrameHeader(pic, enc->profile_, size0);
if (err != VP8_ENC_OK) goto Error;
// All OK.
return 1;
// Error.
Error:
return WebPEncodingSetError(pic, err);
} }
// Segmentation header // Segmentation header
@ -148,7 +264,7 @@ static int EmitPartitionsSize(const VP8Encoder* const enc,
int p; int p;
for (p = 0; p < enc->num_parts_ - 1; ++p) { for (p = 0; p < enc->num_parts_ - 1; ++p) {
const size_t part_size = VP8BitWriterSize(enc->parts_ + p); const size_t part_size = VP8BitWriterSize(enc->parts_ + p);
if (part_size >= MAX_PARTITION_SIZE) { if (part_size >= VP8_MAX_PARTITION_SIZE) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW); return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW);
} }
buf[3 * p + 0] = (part_size >> 0) & 0xff; buf[3 * p + 0] = (part_size >> 0) & 0xff;
@ -164,12 +280,6 @@ static int EmitPartitionsSize(const VP8Encoder* const enc,
#define KTRAILER_SIZE 8 #define KTRAILER_SIZE 8
static void PutLE24(uint8_t* buf, size_t value) {
buf[0] = (value >> 0) & 0xff;
buf[1] = (value >> 8) & 0xff;
buf[2] = (value >> 16) & 0xff;
}
static int WriteExtensions(VP8Encoder* const enc) { static int WriteExtensions(VP8Encoder* const enc) {
uint8_t buffer[KTRAILER_SIZE]; uint8_t buffer[KTRAILER_SIZE];
VP8BitWriter* const bw = &enc->bw_; VP8BitWriter* const bw = &enc->bw_;
@ -186,14 +296,6 @@ static int WriteExtensions(VP8Encoder* const enc) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY); return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
} }
} }
// Alpha (bytes 4..6)
PutLE24(buffer + 4, enc->alpha_data_size_);
if (enc->alpha_data_size_ > 0) {
assert(enc->has_alpha_);
if (!VP8BitWriterAppend(bw, enc->alpha_data_, enc->alpha_data_size_)) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
}
}
buffer[KTRAILER_SIZE - 1] = 0x01; // marker buffer[KTRAILER_SIZE - 1] = 0x01; // marker
if (!VP8BitWriterAppend(bw, buffer, KTRAILER_SIZE)) { if (!VP8BitWriterAppend(bw, buffer, KTRAILER_SIZE)) {
@ -211,7 +313,7 @@ static size_t GeneratePartition0(VP8Encoder* const enc) {
const int mb_size = enc->mb_w_ * enc->mb_h_; const int mb_size = enc->mb_w_ * enc->mb_h_;
uint64_t pos1, pos2, pos3; uint64_t pos1, pos2, pos3;
#ifdef WEBP_EXPERIMENTAL_FEATURES #ifdef WEBP_EXPERIMENTAL_FEATURES
const int need_extensions = enc->has_alpha_ || enc->use_layer_; const int need_extensions = enc->use_layer_;
#endif #endif
pos1 = VP8BitWriterPos(bw); pos1 = VP8BitWriterPos(bw);
@ -250,32 +352,61 @@ static size_t GeneratePartition0(VP8Encoder* const enc) {
return !bw->error_; return !bw->error_;
} }
void VP8EncFreeBitWriters(VP8Encoder* const enc) {
int p;
VP8BitWriterWipeOut(&enc->bw_);
for (p = 0; p < enc->num_parts_; ++p) {
VP8BitWriterWipeOut(enc->parts_ + p);
}
}
int VP8EncWrite(VP8Encoder* const enc) { int VP8EncWrite(VP8Encoder* const enc) {
WebPPicture* const pic = enc->pic_; WebPPicture* const pic = enc->pic_;
VP8BitWriter* const bw = &enc->bw_; VP8BitWriter* const bw = &enc->bw_;
const int task_percent = 19;
const int percent_per_part = task_percent / enc->num_parts_;
const int final_percent = enc->percent_ + task_percent;
int ok = 0; int ok = 0;
size_t coded_size, pad; size_t vp8_size, pad, riff_size;
int p; int p;
// Partition #0 with header and partition sizes // Partition #0 with header and partition sizes
ok = !!GeneratePartition0(enc); ok = !!GeneratePartition0(enc);
// Compute total size (for the RIFF header) // Compute VP8 size
coded_size = KHEADER_SIZE + VP8BitWriterSize(bw) + 3 * (enc->num_parts_ - 1); vp8_size = VP8_FRAME_HEADER_SIZE +
VP8BitWriterSize(bw) +
3 * (enc->num_parts_ - 1);
for (p = 0; p < enc->num_parts_; ++p) { for (p = 0; p < enc->num_parts_; ++p) {
coded_size += VP8BitWriterSize(enc->parts_ + p); vp8_size += VP8BitWriterSize(enc->parts_ + p);
}
pad = vp8_size & 1;
vp8_size += pad;
// Compute RIFF size
// At the minimum it is: "WEBPVP8 nnnn" + VP8 data size.
riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size;
if (IsVP8XNeeded(enc)) { // Add size for: VP8X header + data.
riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
}
if (enc->has_alpha_) { // Add size for: ALPH header + data.
const uint32_t padded_alpha_size = enc->alpha_data_size_ +
(enc->alpha_data_size_ & 1);
riff_size += CHUNK_HEADER_SIZE + padded_alpha_size;
}
// Sanity check.
if (riff_size > 0xfffffffeU) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG);
} }
pad = coded_size & 1;
coded_size += pad;
// Emit headers and partition #0 // Emit headers and partition #0
{ {
const uint8_t* const part0 = VP8BitWriterBuf(bw); const uint8_t* const part0 = VP8BitWriterBuf(bw);
const size_t size0 = VP8BitWriterSize(bw); const size_t size0 = VP8BitWriterSize(bw);
ok = ok && PutHeader(enc->profile_, size0, coded_size, pic) ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size)
&& pic->writer(part0, size0, pic) && pic->writer(part0, size0, pic)
&& EmitPartitionsSize(enc, pic); && EmitPartitionsSize(enc, pic);
free((void*)part0); VP8BitWriterWipeOut(bw); // will free the internal buffer.
} }
// Token partitions // Token partitions
@ -284,16 +415,18 @@ int VP8EncWrite(VP8Encoder* const enc) {
const size_t size = VP8BitWriterSize(enc->parts_ + p); const size_t size = VP8BitWriterSize(enc->parts_ + p);
if (size) if (size)
ok = ok && pic->writer(buf, size, pic); ok = ok && pic->writer(buf, size, pic);
free((void*)buf); VP8BitWriterWipeOut(enc->parts_ + p); // will free the internal buffer.
ok = ok && WebPReportProgress(pic, enc->percent_ + percent_per_part,
&enc->percent_);
} }
// Padding byte // Padding byte
if (ok && pad) { if (ok && pad) {
const uint8_t pad_byte[1] = { 0 }; ok = PutPaddingByte(pic);
ok = pic->writer(pad_byte, 1, pic);
} }
enc->coded_size_ = (int)coded_size + KRIFF_SIZE; enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size);
ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_);
return ok; return ok;
} }

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -9,7 +9,7 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include "vp8enci.h" #include "./vp8enci.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -158,9 +158,12 @@ const uint8_t
void VP8DefaultProbas(VP8Encoder* const enc) { void VP8DefaultProbas(VP8Encoder* const enc) {
VP8Proba* const probas = &enc->proba_; VP8Proba* const probas = &enc->proba_;
probas->use_skip_proba_ = 0;
memset(probas->segments_, 255u, sizeof(probas->segments_)); memset(probas->segments_, 255u, sizeof(probas->segments_));
memcpy(probas->coeffs_, VP8CoeffsProba0, sizeof(VP8CoeffsProba0)); memcpy(probas->coeffs_, VP8CoeffsProba0, sizeof(VP8CoeffsProba0));
probas->use_skip_proba_ = 0; // Note: we could hard-code the level_costs_ corresponding to VP8CoeffsProba0,
// but that's ~11k of static data. Better call VP8CalculateLevelCosts() later.
probas->dirty_ = 1;
} }
// Paragraph 11.5. 900bytes. // Paragraph 11.5. 900bytes.

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -12,7 +12,7 @@
#ifndef WEBP_ENC_VP8ENCI_H_ #ifndef WEBP_ENC_VP8ENCI_H_
#define WEBP_ENC_VP8ENCI_H_ #define WEBP_ENC_VP8ENCI_H_
#include "string.h" // for memcpy() #include <string.h> // for memcpy()
#include "../webp/encode.h" #include "../webp/encode.h"
#include "../dsp/dsp.h" #include "../dsp/dsp.h"
#include "../utils/bit_writer.h" #include "../utils/bit_writer.h"
@ -26,8 +26,8 @@ extern "C" {
// version numbers // version numbers
#define ENC_MAJ_VERSION 0 #define ENC_MAJ_VERSION 0
#define ENC_MIN_VERSION 1 #define ENC_MIN_VERSION 2
#define ENC_REV_VERSION 3 #define ENC_REV_VERSION 0
// size of histogram used by CollectHistogram. // size of histogram used by CollectHistogram.
#define MAX_COEFF_THRESH 64 #define MAX_COEFF_THRESH 64
@ -157,7 +157,7 @@ typedef int64_t score_t; // type used for scores, rate, distortion
#define BIAS(b) ((b) << (QFIX - 8)) #define BIAS(b) ((b) << (QFIX - 8))
// Fun fact: this is the _only_ line where we're actually being lossy and // Fun fact: this is the _only_ line where we're actually being lossy and
// discarding bits. // discarding bits.
static inline int QUANTDIV(int n, int iQ, int B) { static WEBP_INLINE int QUANTDIV(int n, int iQ, int B) {
return (n * iQ + B) >> QFIX; return (n * iQ + B) >> QFIX;
} }
extern const uint8_t VP8Zigzag[16]; extern const uint8_t VP8Zigzag[16];
@ -165,8 +165,9 @@ extern const uint8_t VP8Zigzag[16];
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Headers // Headers
typedef uint32_t proba_t; // 16b + 16b
typedef uint8_t ProbaArray[NUM_CTX][NUM_PROBAS]; typedef uint8_t ProbaArray[NUM_CTX][NUM_PROBAS];
typedef uint64_t StatsArray[NUM_CTX][NUM_PROBAS][2]; typedef proba_t StatsArray[NUM_CTX][NUM_PROBAS];
typedef uint16_t CostArray[NUM_CTX][MAX_VARIABLE_LEVEL + 1]; typedef uint16_t CostArray[NUM_CTX][MAX_VARIABLE_LEVEL + 1];
typedef double LFStats[NUM_MB_SEGMENTS][MAX_LF_LEVELS]; // filter stats typedef double LFStats[NUM_MB_SEGMENTS][MAX_LF_LEVELS]; // filter stats
@ -185,8 +186,9 @@ typedef struct {
uint8_t segments_[3]; // probabilities for segment tree uint8_t segments_[3]; // probabilities for segment tree
uint8_t skip_proba_; // final probability of being skipped. uint8_t skip_proba_; // final probability of being skipped.
ProbaArray coeffs_[NUM_TYPES][NUM_BANDS]; // 924 bytes ProbaArray coeffs_[NUM_TYPES][NUM_BANDS]; // 924 bytes
StatsArray stats_[NUM_TYPES][NUM_BANDS]; // 7.4k StatsArray stats_[NUM_TYPES][NUM_BANDS]; // 4224 bytes
CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 11.4k CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 11.4k
int dirty_; // if true, need to call VP8CalculateLevelCosts()
int use_skip_proba_; // Note: we always use skip_proba for now. int use_skip_proba_; // Note: we always use skip_proba for now.
int nb_skip_; // number of skipped blocks int nb_skip_; // number of skipped blocks
} VP8Proba; } VP8Proba;
@ -241,7 +243,7 @@ typedef struct {
int16_t y_ac_levels[16][16]; int16_t y_ac_levels[16][16];
int16_t uv_levels[4 + 4][16]; int16_t uv_levels[4 + 4][16];
int mode_i16; // mode number for intra16 prediction int mode_i16; // mode number for intra16 prediction
int modes_i4[16]; // mode numbers for intra4 predictions uint8_t modes_i4[16]; // mode numbers for intra4 predictions
int mode_uv; // mode number of chroma prediction int mode_uv; // mode number of chroma prediction
uint32_t nz; // non-zero blocks uint32_t nz; // non-zero blocks
} VP8ModeScore; } VP8ModeScore;
@ -272,6 +274,7 @@ typedef struct {
LFStats* lf_stats_; // filter stats (borrowed from enc_) LFStats* lf_stats_; // filter stats (borrowed from enc_)
int do_trellis_; // if true, perform extra level optimisation int do_trellis_; // if true, perform extra level optimisation
int done_; // true when scan is finished int done_; // true when scan is finished
int percent0_; // saved initial progress percent
} VP8EncIterator; } VP8EncIterator;
// in iterator.c // in iterator.c
@ -288,6 +291,9 @@ void VP8IteratorExport(const VP8EncIterator* const it);
// it->yuv_out_ or it->yuv_in_. // it->yuv_out_ or it->yuv_in_.
int VP8IteratorNext(VP8EncIterator* const it, int VP8IteratorNext(VP8EncIterator* const it,
const uint8_t* const block_to_save); const uint8_t* const block_to_save);
// Report progression based on macroblock rows. Return 0 for user-abort request.
int VP8IteratorProgress(const VP8EncIterator* const it,
int final_delta_percent);
// Intra4x4 iterations // Intra4x4 iterations
void VP8IteratorStartI4(VP8EncIterator* const it); void VP8IteratorStartI4(VP8EncIterator* const it);
// returns true if not done. // returns true if not done.
@ -300,11 +306,52 @@ void VP8IteratorBytesToNz(VP8EncIterator* const it);
// Helper functions to set mode properties // Helper functions to set mode properties
void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode); void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode);
void VP8SetIntra4Mode(const VP8EncIterator* const it, int modes[16]); void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes);
void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode); void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode);
void VP8SetSkip(const VP8EncIterator* const it, int skip); void VP8SetSkip(const VP8EncIterator* const it, int skip);
void VP8SetSegment(const VP8EncIterator* const it, int segment); void VP8SetSegment(const VP8EncIterator* const it, int segment);
void VP8IteratorResetCosts(VP8EncIterator* const it);
//------------------------------------------------------------------------------
// Paginated token buffer
// WIP: #define USE_TOKEN_BUFFER
#ifdef USE_TOKEN_BUFFER
#define MAX_NUM_TOKEN 2048
typedef struct VP8Tokens VP8Tokens;
struct VP8Tokens {
uint16_t tokens_[MAX_NUM_TOKEN]; // bit#15: bit, bits 0..14: slot
int left_;
VP8Tokens* next_;
};
typedef struct {
VP8Tokens* rows_;
uint16_t* tokens_; // set to (*last_)->tokens_
VP8Tokens** last_;
int left_;
int error_; // true in case of malloc error
} VP8TBuffer;
void VP8TBufferInit(VP8TBuffer* const b); // initialize an empty buffer
int VP8TBufferNewPage(VP8TBuffer* const b); // allocate a new page
void VP8TBufferClear(VP8TBuffer* const b); // de-allocate memory
int VP8EmitTokens(const VP8TBuffer* const b, VP8BitWriter* const bw,
const uint8_t* const probas);
static WEBP_INLINE int VP8AddToken(VP8TBuffer* const b,
int bit, int proba_idx) {
if (b->left_ > 0 || VP8TBufferNewPage(b)) {
const int slot = --b->left_;
b->tokens_[slot] = (bit << 15) | proba_idx;
}
return bit;
}
#endif // USE_TOKEN_BUFFER
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// VP8Encoder // VP8Encoder
@ -330,10 +377,12 @@ struct VP8Encoder {
VP8BitWriter bw_; // part0 VP8BitWriter bw_; // part0
VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions
int percent_; // for progress
// transparency blob // transparency blob
int has_alpha_; int has_alpha_;
uint8_t* alpha_data_; // non-NULL if transparency is present uint8_t* alpha_data_; // non-NULL if transparency is present
size_t alpha_data_size_; uint32_t alpha_data_size_;
// enhancement layer // enhancement layer
int use_layer_; int use_layer_;
@ -353,7 +402,7 @@ struct VP8Encoder {
// probabilities and statistics // probabilities and statistics
VP8Proba proba_; VP8Proba proba_;
uint64_t sse_[3]; // sum of Y/U/V squared errors for all macroblocks uint64_t sse_[4]; // sum of Y/U/V/A squared errors for all macroblocks
uint64_t sse_count_; // pixel count for the sse_[] stats uint64_t sse_count_; // pixel count for the sse_[] stats
int coded_size_; int coded_size_;
int residual_bytes_[3][4]; int residual_bytes_[3][4];
@ -401,6 +450,8 @@ void VP8CodeIntraModes(VP8Encoder* const enc);
// and appending an assembly of all the pre-coded token partitions. // and appending an assembly of all the pre-coded token partitions.
// Return true if everything is ok. // Return true if everything is ok.
int VP8EncWrite(VP8Encoder* const enc); int VP8EncWrite(VP8Encoder* const enc);
// Release memory allocated for bit-writing in VP8EncLoop & seq.
void VP8EncFreeBitWriters(VP8Encoder* const enc);
// in frame.c // in frame.c
extern const uint8_t VP8EncBands[16 + 1]; extern const uint8_t VP8EncBands[16 + 1];
@ -421,7 +472,9 @@ int VP8StatLoop(VP8Encoder* const enc);
// in webpenc.c // in webpenc.c
// Assign an error code to a picture. Return false for convenience. // Assign an error code to a picture. Return false for convenience.
int WebPEncodingSetError(WebPPicture* const pic, WebPEncodingError error); int WebPEncodingSetError(const WebPPicture* const pic, WebPEncodingError error);
int WebPReportProgress(const WebPPicture* const pic,
int percent, int* const percent_store);
// in analysis.c // in analysis.c
// Main analysis loop. Decides the segmentations and complexity. // Main analysis loop. Decides the segmentations and complexity.
@ -435,10 +488,9 @@ void VP8SetSegmentParams(VP8Encoder* const enc, float quality);
int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt); int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt);
// in alpha.c // in alpha.c
void VP8EncInitAlpha(VP8Encoder* enc); // initialize alpha compression void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression
void VP8EncCodeAlphaBlock(VP8EncIterator* it); // analyze or code a macroblock int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data
int VP8EncFinishAlpha(VP8Encoder* enc); // finalize compressed data void VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data
void VP8EncDeleteAlpha(VP8Encoder* enc); // delete compressed data
// in layer.c // in layer.c
void VP8EncInitLayer(VP8Encoder* const enc); // init everything void VP8EncInitLayer(VP8Encoder* const enc); // init everything
@ -447,9 +499,22 @@ int VP8EncFinishLayer(VP8Encoder* const enc); // finalize coding
void VP8EncDeleteLayer(VP8Encoder* enc); // reclaim memory void VP8EncDeleteLayer(VP8Encoder* enc); // reclaim memory
// in filter.c // in filter.c
extern void VP8InitFilter(VP8EncIterator* const it);
extern void VP8StoreFilterStats(VP8EncIterator* const it); // SSIM utils
extern void VP8AdjustFilterStrength(VP8EncIterator* const it); typedef struct {
double w, xm, ym, xxm, xym, yym;
} DistoStats;
void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst);
void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1,
const uint8_t* src2, int stride2,
int W, int H, DistoStats* const stats);
double VP8SSIMGet(const DistoStats* const stats);
double VP8SSIMGetSquaredError(const DistoStats* const stats);
// autofilter
void VP8InitFilter(VP8EncIterator* const it);
void VP8StoreFilterStats(VP8EncIterator* const it);
void VP8AdjustFilterStrength(VP8EncIterator* const it);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

1150
src/enc/vp8l.c Normal file

File diff suppressed because it is too large Load Diff

68
src/enc/vp8li.h Normal file
View File

@ -0,0 +1,68 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Lossless encoder: internal header.
//
// Author: Vikas Arora (vikaas.arora@gmail.com)
#ifndef WEBP_ENC_VP8LI_H_
#define WEBP_ENC_VP8LI_H_
#include "./histogram.h"
#include "../utils/bit_writer.h"
#include "../webp/encode.h"
#include "../webp/format_constants.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
typedef struct {
const WebPConfig* config_; // user configuration and parameters
const WebPPicture* pic_; // input picture.
uint32_t* argb_; // Transformed argb image data.
uint32_t* argb_scratch_; // Scratch memory for argb rows
// (used for prediction).
uint32_t* transform_data_; // Scratch memory for transform data.
int current_width_; // Corresponds to packed image width.
// Encoding parameters derived from quality parameter.
int histo_bits_;
int transform_bits_;
int cache_bits_; // If equal to 0, don't use color cache.
// Encoding parameters derived from image characteristics.
int use_cross_color_;
int use_subtract_green_;
int use_predict_;
int use_palette_;
int palette_size_;
uint32_t palette_[MAX_PALETTE_SIZE];
} VP8LEncoder;
//------------------------------------------------------------------------------
// internal functions. Not public.
// Encodes the picture.
// Returns 0 if config or picture is NULL or picture doesn't have valid argb
// input.
int VP8LEncodeImage(const WebPConfig* const config,
const WebPPicture* const picture);
// Encodes the main image stream using the supplied bit writer.
WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
const WebPPicture* const picture,
VP8LBitWriter* const bw);
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
#endif /* WEBP_ENC_VP8LI_H_ */

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -14,7 +14,9 @@
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include "vp8enci.h" #include "./vp8enci.h"
#include "./vp8li.h"
#include "../utils/utils.h"
// #define PRINT_MEMORY_INFO // #define PRINT_MEMORY_INFO
@ -45,11 +47,11 @@ static int DummyWriter(const uint8_t* data, size_t data_size,
return 1; return 1;
} }
int WebPPictureInitInternal(WebPPicture* const picture, int version) { int WebPPictureInitInternal(WebPPicture* picture, int version) {
if (version != WEBP_ENCODER_ABI_VERSION) { if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
return 0; // caller/system version mismatch! return 0; // caller/system version mismatch!
} }
if (picture) { if (picture != NULL) {
memset(picture, 0, sizeof(*picture)); memset(picture, 0, sizeof(*picture));
picture->writer = DummyWriter; picture->writer = DummyWriter;
WebPEncodingSetError(picture, VP8_ENC_OK); WebPEncodingSetError(picture, VP8_ENC_OK);
@ -142,8 +144,8 @@ static void MapConfigToTools(VP8Encoder* const enc) {
// LFStats: 2048 // LFStats: 2048
// Picture size (yuv): 589824 // Picture size (yuv): 589824
static VP8Encoder* InitEncoder(const WebPConfig* const config, static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
WebPPicture* const picture) { WebPPicture* const picture) {
const int use_filter = const int use_filter =
(config->filter_strength > 0) || (config->autofilter > 0); (config->filter_strength > 0) || (config->autofilter > 0);
const int mb_w = (picture->width + 15) >> 4; const int mb_w = (picture->width + 15) >> 4;
@ -163,13 +165,14 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config,
config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0; config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0;
VP8Encoder* enc; VP8Encoder* enc;
uint8_t* mem; uint8_t* mem;
size_t size = sizeof(VP8Encoder) + ALIGN_CST // main struct const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct
+ cache_size // working caches + ALIGN_CST // cache alignment
+ info_size // modes info + cache_size // working caches
+ preds_size // prediction modes + info_size // modes info
+ samples_size // top/left samples + preds_size // prediction modes
+ nz_size // coeff context bits + samples_size // top/left samples
+ lf_stats_size; // autofilter stats + nz_size // coeff context bits
+ lf_stats_size; // autofilter stats
#ifdef PRINT_MEMORY_INFO #ifdef PRINT_MEMORY_INFO
printf("===================================\n"); printf("===================================\n");
@ -197,7 +200,7 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config,
mb_w * mb_h * 384 * sizeof(uint8_t)); mb_w * mb_h * 384 * sizeof(uint8_t));
printf("===================================\n"); printf("===================================\n");
#endif #endif
mem = (uint8_t*)malloc(size); mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem));
if (mem == NULL) { if (mem == NULL) {
WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
return NULL; return NULL;
@ -242,6 +245,7 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config,
enc->config_ = config; enc->config_ = config;
enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2; enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
enc->pic_ = picture; enc->pic_ = picture;
enc->percent_ = 0;
MapConfigToTools(enc); MapConfigToTools(enc);
VP8EncDspInit(); VP8EncDspInit();
@ -250,18 +254,18 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config,
ResetFilterHeader(enc); ResetFilterHeader(enc);
ResetBoundaryPredictions(enc); ResetBoundaryPredictions(enc);
#ifdef WEBP_EXPERIMENTAL_FEATURES
VP8EncInitAlpha(enc); VP8EncInitAlpha(enc);
#ifdef WEBP_EXPERIMENTAL_FEATURES
VP8EncInitLayer(enc); VP8EncInitLayer(enc);
#endif #endif
return enc; return enc;
} }
static void DeleteEncoder(VP8Encoder* enc) { static void DeleteVP8Encoder(VP8Encoder* enc) {
if (enc) { if (enc != NULL) {
#ifdef WEBP_EXPERIMENTAL_FEATURES
VP8EncDeleteAlpha(enc); VP8EncDeleteAlpha(enc);
#ifdef WEBP_EXPERIMENTAL_FEATURES
VP8EncDeleteLayer(enc); VP8EncDeleteLayer(enc);
#endif #endif
free(enc); free(enc);
@ -282,11 +286,12 @@ static void FinalizePSNR(const VP8Encoder* const enc) {
stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4); stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4); stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2); stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
stats->PSNR[4] = (float)GetPSNR(sse[3], size);
} }
static void StoreStats(VP8Encoder* const enc) { static void StoreStats(VP8Encoder* const enc) {
WebPAuxStats* const stats = enc->pic_->stats; WebPAuxStats* const stats = enc->pic_->stats;
if (stats) { if (stats != NULL) {
int i, s; int i, s;
for (i = 0; i < NUM_MB_SEGMENTS; ++i) { for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
stats->segment_level[i] = enc->dqm_[i].fstrength_; stats->segment_level[i] = enc->dqm_[i].fstrength_;
@ -301,19 +306,32 @@ static void StoreStats(VP8Encoder* const enc) {
stats->block_count[i] = enc->block_count_[i]; stats->block_count[i] = enc->block_count_[i];
} }
} }
WebPReportProgress(enc->pic_, 100, &enc->percent_); // done!
} }
int WebPEncodingSetError(WebPPicture* const pic, WebPEncodingError error) { int WebPEncodingSetError(const WebPPicture* const pic,
assert((int)error <= VP8_ENC_ERROR_BAD_WRITE); WebPEncodingError error) {
assert((int)error < VP8_ENC_ERROR_LAST);
assert((int)error >= VP8_ENC_OK); assert((int)error >= VP8_ENC_OK);
pic->error_code = error; ((WebPPicture*)pic)->error_code = error;
return 0; return 0;
} }
int WebPReportProgress(const WebPPicture* const pic,
int percent, int* const percent_store) {
if (percent_store != NULL && percent != *percent_store) {
*percent_store = percent;
if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
// user abort requested
WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
return 0;
}
}
return 1; // ok
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
VP8Encoder* enc;
int ok; int ok;
if (pic == NULL) if (pic == NULL)
@ -325,23 +343,43 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
if (pic->width <= 0 || pic->height <= 0) if (pic->width <= 0 || pic->height <= 0)
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
if (pic->y == NULL || pic->u == NULL || pic->v == NULL)
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION)
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
enc = InitEncoder(config, pic); if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats));
if (enc == NULL) return 0; // pic->error is already set.
ok = VP8EncAnalyze(enc) if (!config->lossless) {
&& VP8StatLoop(enc) VP8Encoder* enc = NULL;
&& VP8EncLoop(enc) if (pic->y == NULL || pic->u == NULL || pic->v == NULL) {
if (pic->argb != NULL) {
if (!WebPPictureARGBToYUVA(pic, WEBP_YUV420)) return 0;
} else {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
}
}
enc = InitVP8Encoder(config, pic);
if (enc == NULL) return 0; // pic->error is already set.
// Note: each of the tasks below account for 20% in the progress report.
ok = VP8EncAnalyze(enc)
&& VP8StatLoop(enc)
&& VP8EncLoop(enc)
&& VP8EncFinishAlpha(enc)
#ifdef WEBP_EXPERIMENTAL_FEATURES #ifdef WEBP_EXPERIMENTAL_FEATURES
&& VP8EncFinishAlpha(enc) && VP8EncFinishLayer(enc)
&& VP8EncFinishLayer(enc)
#endif #endif
&& VP8EncWrite(enc); && VP8EncWrite(enc);
StoreStats(enc); StoreStats(enc);
DeleteEncoder(enc); if (!ok) {
VP8EncFreeBitWriters(enc);
}
DeleteVP8Encoder(enc);
} else {
if (pic->argb == NULL)
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem.
}
return ok; return ok;
} }

16
src/mux/Makefile.am Normal file
View File

@ -0,0 +1,16 @@
AM_CPPFLAGS = -I$(top_srcdir)/src
lib_LTLIBRARIES = libwebpmux.la
libwebpmux_la_SOURCES =
libwebpmux_la_SOURCES += demux.c
libwebpmux_la_SOURCES += muxedit.c
libwebpmux_la_SOURCES += muxi.h
libwebpmux_la_SOURCES += muxinternal.c
libwebpmux_la_SOURCES += muxread.c
libwebpmuxinclude_HEADERS =
libwebpmuxinclude_HEADERS += ../webp/mux.h
libwebpmuxinclude_HEADERS += ../webp/types.h
libwebpmux_la_LDFLAGS = -version-info 0:0:0
libwebpmuxincludedir = $(includedir)/webp

902
src/mux/demux.c Normal file
View File

@ -0,0 +1,902 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// WebP container demux.
//
#include "../webp/mux.h"
#include <stdlib.h>
#include <string.h>
#include "../webp/decode.h" // WebPGetInfo
#include "../webp/format_constants.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#define MKFOURCC(a, b, c, d) ((uint32_t)(a) | (b) << 8 | (c) << 16 | (d) << 24)
typedef struct {
size_t start_; // start location of the data
size_t end_; // end location
size_t riff_end_; // riff chunk end location, can be > end_.
size_t buf_size_; // size of the buffer
const uint8_t* buf_;
} MemBuffer;
typedef struct {
size_t offset_;
size_t size_;
} ChunkData;
typedef struct Frame {
int x_offset_, y_offset_;
int width_, height_;
int duration_;
int is_tile_; // this is an image fragment from a 'TILE'.
int frame_num_; // the referent frame number for use in assembling tiles.
int complete_; // img_components_ contains a full image.
ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH
struct Frame* next_;
} Frame;
typedef struct Chunk {
ChunkData data_;
struct Chunk* next_;
} Chunk;
struct WebPDemuxer {
MemBuffer mem_;
WebPDemuxState state_;
int is_ext_format_;
uint32_t feature_flags_;
int canvas_width_, canvas_height_;
int loop_count_;
int num_frames_;
Frame* frames_;
Chunk* chunks_; // non-image chunks
};
typedef enum {
PARSE_OK,
PARSE_NEED_MORE_DATA,
PARSE_ERROR
} ParseStatus;
typedef struct ChunkParser {
uint8_t id[4];
ParseStatus (*parse)(WebPDemuxer* const dmux);
int (*valid)(const WebPDemuxer* const dmux);
} ChunkParser;
static ParseStatus ParseSingleImage(WebPDemuxer* const dmux);
static ParseStatus ParseVP8X(WebPDemuxer* const dmux);
static int IsValidSimpleFormat(const WebPDemuxer* const dmux);
static int IsValidExtendedFormat(const WebPDemuxer* const dmux);
static const ChunkParser kMasterChunks[] = {
{ { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat },
{ { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat },
{ { 'V', 'P', '8', 'X' }, ParseVP8X, IsValidExtendedFormat },
{ { '0', '0', '0', '0' }, NULL, NULL },
};
// -----------------------------------------------------------------------------
// MemBuffer
static int RemapMemBuffer(MemBuffer* const mem,
const uint8_t* data, size_t size) {
if (size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
mem->buf_ = data;
mem->end_ = mem->buf_size_ = size;
return 1;
}
static int InitMemBuffer(MemBuffer* const mem,
const uint8_t* data, size_t size) {
memset(mem, 0, sizeof(*mem));
return RemapMemBuffer(mem, data, size);
}
// Return the remaining data size available in 'mem'.
static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {
return (mem->end_ - mem->start_);
}
// Return true if 'size' exceeds the end of the RIFF chunk.
static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
return (size > mem->riff_end_ - mem->start_);
}
static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
mem->start_ += size;
}
static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
mem->start_ -= size;
}
static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {
return mem->buf_ + mem->start_;
}
static WEBP_INLINE uint8_t GetByte(MemBuffer* const mem) {
const uint8_t byte = mem->buf_[mem->start_];
Skip(mem, 1);
return byte;
}
// Read 16, 24 or 32 bits stored in little-endian order.
static WEBP_INLINE int ReadLE16s(const uint8_t* const data) {
return (int)(data[0] << 0) | (data[1] << 8);
}
static WEBP_INLINE int ReadLE24s(const uint8_t* const data) {
return ReadLE16s(data) | (data[2] << 16);
}
static WEBP_INLINE uint32_t ReadLE32(const uint8_t* const data) {
return (uint32_t)ReadLE24s(data) | (data[3] << 24);
}
// In addition to reading, skip the read bytes.
static WEBP_INLINE int GetLE16s(MemBuffer* const mem) {
const uint8_t* const data = mem->buf_ + mem->start_;
const int val = ReadLE16s(data);
Skip(mem, 2);
return val;
}
static WEBP_INLINE int GetLE24s(MemBuffer* const mem) {
const uint8_t* const data = mem->buf_ + mem->start_;
const int val = ReadLE24s(data);
Skip(mem, 3);
return val;
}
static WEBP_INLINE uint32_t GetLE32(MemBuffer* const mem) {
const uint8_t* const data = mem->buf_ + mem->start_;
const uint32_t val = ReadLE32(data);
Skip(mem, 4);
return val;
}
// -----------------------------------------------------------------------------
// Secondary chunk parsing
static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
Chunk** c = &dmux->chunks_;
while (*c != NULL) c = &(*c)->next_;
*c = chunk;
chunk->next_ = NULL;
}
// Add a frame to the end of the list, ensuring the last frame is complete.
// Returns true on success, false otherwise.
static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
const Frame* last_frame = NULL;
Frame** f = &dmux->frames_;
while (*f != NULL) {
last_frame = *f;
f = &(*f)->next_;
}
if (last_frame != NULL && !last_frame->complete_) return 0;
*f = frame;
frame->next_ = NULL;
return 1;
}
// Store image bearing chunks to 'frame'.
static ParseStatus StoreFrame(int frame_num, MemBuffer* const mem,
Frame* const frame) {
int alpha_chunks = 0;
int image_chunks = 0;
int done = (MemDataSize(mem) < CHUNK_HEADER_SIZE);
ParseStatus status = PARSE_OK;
if (done) return PARSE_NEED_MORE_DATA;
do {
const size_t chunk_start_offset = mem->start_;
const uint32_t fourcc = GetLE32(mem);
const uint32_t payload_size = GetLE32(mem);
const uint32_t payload_size_padded = payload_size + (payload_size & 1);
const size_t payload_available = (payload_size_padded > MemDataSize(mem))
? MemDataSize(mem) : payload_size_padded;
const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available;
if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
switch (fourcc) {
case MKFOURCC('A', 'L', 'P', 'H'):
if (alpha_chunks == 0) {
++alpha_chunks;
frame->img_components_[1].offset_ = chunk_start_offset;
frame->img_components_[1].size_ = chunk_size;
frame->frame_num_ = frame_num;
Skip(mem, payload_available);
} else {
goto Done;
}
break;
case MKFOURCC('V', 'P', '8', ' '):
case MKFOURCC('V', 'P', '8', 'L'):
if (image_chunks == 0) {
int width = 0, height = 0;
++image_chunks;
frame->img_components_[0].offset_ = chunk_start_offset;
frame->img_components_[0].size_ = chunk_size;
// Extract the width and height from the bitstream, tolerating
// failures when the data is incomplete.
if (!WebPGetInfo(mem->buf_ + frame->img_components_[0].offset_,
frame->img_components_[0].size_, &width, &height) &&
status != PARSE_NEED_MORE_DATA) {
return PARSE_ERROR;
}
frame->width_ = width;
frame->height_ = height;
frame->frame_num_ = frame_num;
frame->complete_ = (status == PARSE_OK);
Skip(mem, payload_available);
} else {
goto Done;
}
break;
Done:
default:
// Restore fourcc/size when moving up one level in parsing.
Rewind(mem, CHUNK_HEADER_SIZE);
done = 1;
break;
}
if (mem->start_ == mem->riff_end_) {
done = 1;
} else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
status = PARSE_NEED_MORE_DATA;
}
} while (!done && status == PARSE_OK);
return status;
}
// Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
// enough data ('min_size') to parse the payload.
// Returns PARSE_OK on success with *frame pointing to the new Frame.
// Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
static ParseStatus NewFrame(const MemBuffer* const mem,
uint32_t min_size, uint32_t expected_size,
uint32_t actual_size, Frame** frame) {
if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
if (actual_size < expected_size) return PARSE_ERROR;
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
*frame = (Frame*)calloc(1, sizeof(**frame));
return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
}
// Parse a 'FRM ' chunk and any image bearing chunks that immediately follow.
// 'frame_chunk_size' is the previously validated, padded chunk size.
static ParseStatus ParseFrame(
WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
const uint32_t min_size = frame_chunk_size + CHUNK_HEADER_SIZE;
int added_frame = 0;
MemBuffer* const mem = &dmux->mem_;
Frame* frame;
ParseStatus status =
NewFrame(mem, min_size, FRAME_CHUNK_SIZE, frame_chunk_size, &frame);
if (status != PARSE_OK) return status;
frame->x_offset_ = 2 * GetLE24s(mem);
frame->y_offset_ = 2 * GetLE24s(mem);
frame->width_ = 1 + GetLE24s(mem);
frame->height_ = 1 + GetLE24s(mem);
frame->duration_ = 1 + GetLE24s(mem);
Skip(mem, frame_chunk_size - FRAME_CHUNK_SIZE); // skip any trailing data.
if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
return PARSE_ERROR;
}
// Store a (potentially partial) frame only if the animation flag is set
// and there is some data in 'frame'.
status = StoreFrame(dmux->num_frames_ + 1, mem, frame);
if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) {
added_frame = AddFrame(dmux, frame);
if (added_frame) {
++dmux->num_frames_;
} else {
status = PARSE_ERROR;
}
}
if (!added_frame) free(frame);
return status;
}
// Parse a 'TILE' chunk and any image bearing chunks that immediately follow.
// 'tile_chunk_size' is the previously validated, padded chunk size.
static ParseStatus ParseTile(WebPDemuxer* const dmux,
uint32_t tile_chunk_size) {
const int has_tiles = !!(dmux->feature_flags_ & TILE_FLAG);
const uint32_t min_size = tile_chunk_size + CHUNK_HEADER_SIZE;
int added_tile = 0;
MemBuffer* const mem = &dmux->mem_;
Frame* frame;
ParseStatus status =
NewFrame(mem, min_size, TILE_CHUNK_SIZE, tile_chunk_size, &frame);
if (status != PARSE_OK) return status;
frame->is_tile_ = 1;
frame->x_offset_ = 2 * GetLE24s(mem);
frame->y_offset_ = 2 * GetLE24s(mem);
Skip(mem, tile_chunk_size - TILE_CHUNK_SIZE); // skip any trailing data.
// Store a (potentially partial) tile only if the tile flag is set
// and the tile contains some data.
status = StoreFrame(dmux->num_frames_, mem, frame);
if (status != PARSE_ERROR && has_tiles && frame->frame_num_ > 0) {
// Note num_frames_ is incremented only when all tiles have been consumed.
added_tile = AddFrame(dmux, frame);
if (!added_tile) status = PARSE_ERROR;
}
if (!added_tile) free(frame);
return status;
}
// General chunk storage starting with the header at 'start_offset' allowing
// the user to request the payload via a fourcc string. 'size' includes the
// header and the unpadded payload size.
// Returns true on success, false otherwise.
static int StoreChunk(WebPDemuxer* const dmux,
size_t start_offset, uint32_t size) {
Chunk* const chunk = (Chunk*)calloc(1, sizeof(*chunk));
if (chunk == NULL) return 0;
chunk->data_.offset_ = start_offset;
chunk->data_.size_ = size;
AddChunk(dmux, chunk);
return 1;
}
// -----------------------------------------------------------------------------
// Primary chunk parsing
static int ReadHeader(MemBuffer* const mem) {
const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
uint32_t riff_size;
// Basic file level validation.
if (MemDataSize(mem) < min_size) return 0;
if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
return 0;
}
riff_size = ReadLE32(GetBuffer(mem) + TAG_SIZE);
if (riff_size < CHUNK_HEADER_SIZE) return 0;
if (riff_size > MAX_CHUNK_PAYLOAD) return 0;
// There's no point in reading past the end of the RIFF chunk
mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE;
if (mem->buf_size_ > mem->riff_end_) {
mem->buf_size_ = mem->end_ = mem->riff_end_;
}
Skip(mem, RIFF_HEADER_SIZE);
return 1;
}
static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
const size_t min_size = CHUNK_HEADER_SIZE;
MemBuffer* const mem = &dmux->mem_;
Frame* frame;
ParseStatus status;
if (dmux->frames_ != NULL) return PARSE_ERROR;
if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
frame = (Frame*)calloc(1, sizeof(*frame));
if (frame == NULL) return PARSE_ERROR;
status = StoreFrame(1, &dmux->mem_, frame);
if (status != PARSE_ERROR) {
const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
// Clear any alpha when the alpha flag is missing.
if (!has_alpha && frame->img_components_[1].size_ > 0) {
frame->img_components_[1].offset_ = 0;
frame->img_components_[1].size_ = 0;
}
// Use the frame width/height as the canvas values for non-vp8x files.
if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) {
dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
dmux->canvas_width_ = frame->width_;
dmux->canvas_height_ = frame->height_;
}
AddFrame(dmux, frame);
dmux->num_frames_ = 1;
} else {
free(frame);
}
return status;
}
static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
MemBuffer* const mem = &dmux->mem_;
int loop_chunks = 0;
uint32_t vp8x_size;
ParseStatus status = PARSE_OK;
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
dmux->is_ext_format_ = 1;
Skip(mem, TAG_SIZE); // VP8X
vp8x_size = GetLE32(mem);
if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
vp8x_size += vp8x_size & 1;
if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
dmux->feature_flags_ = GetByte(mem);
Skip(mem, 3); // Reserved.
dmux->canvas_width_ = 1 + GetLE24s(mem);
dmux->canvas_height_ = 1 + GetLE24s(mem);
if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
return PARSE_ERROR; // image final dimension is too large
}
Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.
dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
do {
int store_chunk = 1;
const size_t chunk_start_offset = mem->start_;
const uint32_t fourcc = GetLE32(mem);
const uint32_t chunk_size = GetLE32(mem);
const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1);
if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
switch (fourcc) {
case MKFOURCC('V', 'P', '8', 'X'): {
return PARSE_ERROR;
}
case MKFOURCC('A', 'L', 'P', 'H'):
case MKFOURCC('V', 'P', '8', ' '):
case MKFOURCC('V', 'P', '8', 'L'): {
Rewind(mem, CHUNK_HEADER_SIZE);
status = ParseSingleImage(dmux);
break;
}
case MKFOURCC('L', 'O', 'O', 'P'): {
if (chunk_size_padded < LOOP_CHUNK_SIZE) return PARSE_ERROR;
if (MemDataSize(mem) < chunk_size_padded) {
status = PARSE_NEED_MORE_DATA;
} else if (loop_chunks == 0) {
++loop_chunks;
dmux->loop_count_ = GetLE16s(mem);
Skip(mem, chunk_size_padded - LOOP_CHUNK_SIZE);
} else {
store_chunk = 0;
goto Skip;
}
break;
}
case MKFOURCC('F', 'R', 'M', ' '): {
status = ParseFrame(dmux, chunk_size_padded);
break;
}
case MKFOURCC('T', 'I', 'L', 'E'): {
if (dmux->num_frames_ == 0) dmux->num_frames_ = 1;
status = ParseTile(dmux, chunk_size_padded);
break;
}
case MKFOURCC('I', 'C', 'C', 'P'): {
store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
goto Skip;
}
case MKFOURCC('M', 'E', 'T', 'A'): {
store_chunk = !!(dmux->feature_flags_ & META_FLAG);
goto Skip;
}
Skip:
default: {
if (chunk_size_padded <= MemDataSize(mem)) {
if (store_chunk) {
// Store only the chunk header and unpadded size as only the payload
// will be returned to the user.
if (!StoreChunk(dmux, chunk_start_offset,
CHUNK_HEADER_SIZE + chunk_size)) {
return PARSE_ERROR;
}
}
Skip(mem, chunk_size_padded);
} else {
status = PARSE_NEED_MORE_DATA;
}
}
}
if (mem->start_ == mem->riff_end_) {
break;
} else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
status = PARSE_NEED_MORE_DATA;
}
} while (status == PARSE_OK);
return status;
}
// -----------------------------------------------------------------------------
// Format validation
static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
const Frame* const frame = dmux->frames_;
if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0;
if (frame->width_ <= 0 || frame->height_ <= 0) return 0;
return 1;
}
static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
const int has_tiles = !!(dmux->feature_flags_ & TILE_FLAG);
const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
const Frame* f;
if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
if (dmux->loop_count_ < 0) return 0;
if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
for (f = dmux->frames_; f != NULL; f = f->next_) {
const int cur_frame_set = f->frame_num_;
int frame_count = 0, tile_count = 0;
// Check frame properties and if the image is composed of tiles that each
// fragment came from a 'TILE'.
for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
const ChunkData* const image = f->img_components_;
const ChunkData* const alpha = f->img_components_ + 1;
if (!has_tiles && f->is_tile_) return 0;
if (!has_frames && f->frame_num_ > 1) return 0;
if (f->x_offset_ < 0 || f->y_offset_ < 0) return 0;
if (f->complete_) {
if (alpha->size_ == 0 && image->size_ == 0) return 0;
// Ensure alpha precedes image bitstream.
if (alpha->size_ > 0 && alpha->offset_ > image->offset_) {
return 0;
}
if (f->width_ <= 0 || f->height_ <= 0) return 0;
} else {
// Ensure alpha precedes image bitstream.
if (alpha->size_ > 0 && image->size_ > 0 &&
alpha->offset_ > image->offset_) {
return 0;
}
// There shouldn't be any frames after an incomplete one.
if (f->next_ != NULL) return 0;
}
tile_count += f->is_tile_;
++frame_count;
}
if (!has_tiles && frame_count > 1) return 0;
if (tile_count > 0 && frame_count != tile_count) return 0;
if (f == NULL) break;
}
return 1;
}
// -----------------------------------------------------------------------------
// WebPDemuxer object
static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
dmux->loop_count_ = 1;
dmux->canvas_width_ = -1;
dmux->canvas_height_ = -1;
dmux->mem_ = *mem;
}
WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
WebPDemuxState* state, int version) {
const ChunkParser* parser;
int partial;
ParseStatus status = PARSE_ERROR;
MemBuffer mem;
WebPDemuxer* dmux;
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
if (data == NULL || data->bytes_ == NULL || data->size_ == 0) return NULL;
if (!InitMemBuffer(&mem, data->bytes_, data->size_)) return NULL;
if (!ReadHeader(&mem)) return NULL;
partial = (mem.buf_size_ < mem.riff_end_);
if (!allow_partial && partial) return NULL;
dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux));
if (dmux == NULL) return NULL;
InitDemux(dmux, &mem);
for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) {
status = parser->parse(dmux);
if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE;
if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
break;
}
}
if (state) *state = dmux->state_;
if (status == PARSE_ERROR) {
WebPDemuxDelete(dmux);
return NULL;
}
return dmux;
}
void WebPDemuxDelete(WebPDemuxer* dmux) {
Chunk* c;
Frame* f;
if (dmux == NULL) return;
for (f = dmux->frames_; f != NULL;) {
Frame* const cur_frame = f;
f = f->next_;
free(cur_frame);
}
for (c = dmux->chunks_; c != NULL;) {
Chunk* const cur_chunk = c;
c = c->next_;
free(cur_chunk);
}
free(dmux);
}
// -----------------------------------------------------------------------------
uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
if (dmux == NULL) return 0;
switch (feature) {
case WEBP_FF_FORMAT_FLAGS: return dmux->feature_flags_;
case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width_;
case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height_;
case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count_;
}
return 0;
}
// -----------------------------------------------------------------------------
// Frame iteration
// Find the first 'frame_num' frame. There may be multiple in a tiled frame.
static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
const Frame* f;
for (f = dmux->frames_; f != NULL; f = f->next_) {
if (frame_num == f->frame_num_) break;
}
return f;
}
// Returns tile 'tile_num' and the total count.
static const Frame* GetTile(
const Frame* const frame_set, int tile_num, int* const count) {
const int this_frame = frame_set->frame_num_;
const Frame* f = frame_set;
const Frame* tile = NULL;
int total;
for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) {
if (++total == tile_num) tile = f;
}
*count = total;
return tile;
}
static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
const Frame* const frame,
size_t* const data_size) {
*data_size = 0;
if (frame != NULL) {
const ChunkData* const image = frame->img_components_;
const ChunkData* const alpha = frame->img_components_ + 1;
size_t start_offset = image->offset_;
*data_size = image->size_;
// if alpha exists it precedes image, update the size allowing for
// intervening chunks.
if (alpha->size_ > 0) {
const size_t inter_size = (image->offset_ > 0)
? image->offset_ - (alpha->offset_ + alpha->size_)
: 0;
start_offset = alpha->offset_;
*data_size += alpha->size_ + inter_size;
}
return mem_buf + start_offset;
}
return NULL;
}
// Create a whole 'frame' from VP8 (+ alpha) or lossless.
static int SynthesizeFrame(const WebPDemuxer* const dmux,
const Frame* const first_frame,
int tile_num, WebPIterator* const iter) {
const uint8_t* const mem_buf = dmux->mem_.buf_;
int num_tiles;
size_t payload_size = 0;
const Frame* const tile = GetTile(first_frame, tile_num, &num_tiles);
const uint8_t* const payload = GetFramePayload(mem_buf, tile, &payload_size);
if (payload == NULL) return 0;
iter->frame_num_ = first_frame->frame_num_;
iter->num_frames_ = dmux->num_frames_;
iter->tile_num_ = tile_num;
iter->num_tiles_ = num_tiles;
iter->x_offset_ = tile->x_offset_;
iter->y_offset_ = tile->y_offset_;
iter->width_ = tile->width_;
iter->height_ = tile->height_;
iter->duration_ = tile->duration_;
iter->complete_ = tile->complete_;
iter->tile_.bytes_ = payload;
iter->tile_.size_ = payload_size;
// TODO(jzern): adjust offsets for 'TILE's embedded in 'FRM 's
return 1;
}
static int SetFrame(int frame_num, WebPIterator* const iter) {
const Frame* frame;
const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
if (dmux == NULL || frame_num < 0) return 0;
if (frame_num > dmux->num_frames_) return 0;
if (frame_num == 0) frame_num = dmux->num_frames_;
frame = GetFrame(dmux, frame_num);
return SynthesizeFrame(dmux, frame, 1, iter);
}
int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
if (iter == NULL) return 0;
memset(iter, 0, sizeof(*iter));
iter->private_ = (void*)dmux;
return SetFrame(frame, iter);
}
int WebPDemuxNextFrame(WebPIterator* iter) {
if (iter == NULL) return 0;
return SetFrame(iter->frame_num_ + 1, iter);
}
int WebPDemuxPrevFrame(WebPIterator* iter) {
if (iter == NULL) return 0;
if (iter->frame_num_ <= 1) return 0;
return SetFrame(iter->frame_num_ - 1, iter);
}
int WebPDemuxSelectTile(WebPIterator* iter, int tile) {
if (iter != NULL && iter->private_ != NULL && tile > 0) {
const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
const Frame* const frame = GetFrame(dmux, iter->frame_num_);
if (frame == NULL) return 0;
return SynthesizeFrame(dmux, frame, tile, iter);
}
return 0;
}
void WebPDemuxReleaseIterator(WebPIterator* iter) {
(void)iter;
}
// -----------------------------------------------------------------------------
// Chunk iteration
static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
const uint8_t* const mem_buf = dmux->mem_.buf_;
const Chunk* c;
int count = 0;
for (c = dmux->chunks_; c != NULL; c = c->next_) {
const uint8_t* const header = mem_buf + c->data_.offset_;
if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
}
return count;
}
static const Chunk* GetChunk(const WebPDemuxer* const dmux,
const char fourcc[4], int chunk_num) {
const uint8_t* const mem_buf = dmux->mem_.buf_;
const Chunk* c;
int count = 0;
for (c = dmux->chunks_; c != NULL; c = c->next_) {
const uint8_t* const header = mem_buf + c->data_.offset_;
if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
if (count == chunk_num) break;
}
return c;
}
static int SetChunk(const char fourcc[4], int chunk_num,
WebPChunkIterator* const iter) {
const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
int count;
if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;
count = ChunkCount(dmux, fourcc);
if (count == 0) return 0;
if (chunk_num == 0) chunk_num = count;
if (chunk_num <= count) {
const uint8_t* const mem_buf = dmux->mem_.buf_;
const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
iter->chunk_.bytes_ = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE;
iter->chunk_.size_ = chunk->data_.size_ - CHUNK_HEADER_SIZE;
iter->num_chunks_ = count;
iter->chunk_num_ = chunk_num;
return 1;
}
return 0;
}
int WebPDemuxGetChunk(const WebPDemuxer* dmux,
const char fourcc[4], int chunk_num,
WebPChunkIterator* iter) {
if (iter == NULL) return 0;
memset(iter, 0, sizeof(*iter));
iter->private_ = (void*)dmux;
return SetChunk(fourcc, chunk_num, iter);
}
int WebPDemuxNextChunk(WebPChunkIterator* iter) {
if (iter != NULL) {
const char* const fourcc =
(const char*)iter->chunk_.bytes_ - CHUNK_HEADER_SIZE;
return SetChunk(fourcc, iter->chunk_num_ + 1, iter);
}
return 0;
}
int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
if (iter != NULL && iter->chunk_num_ > 1) {
const char* const fourcc =
(const char*)iter->chunk_.bytes_ - CHUNK_HEADER_SIZE;
return SetChunk(fourcc, iter->chunk_num_ - 1, iter);
}
return 0;
}
void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
(void)iter;
}
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

712
src/mux/muxedit.c Normal file
View File

@ -0,0 +1,712 @@
// Copyright 2011 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Set and delete APIs for mux.
//
// Authors: Urvang (urvang@google.com)
// Vikas (vikasa@google.com)
#include <assert.h>
#include "./muxi.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------
// Life of a mux object.
static void MuxInit(WebPMux* const mux) {
if (mux == NULL) return;
memset(mux, 0, sizeof(*mux));
}
WebPMux* WebPNewInternal(int version) {
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) {
return NULL;
} else {
WebPMux* const mux = (WebPMux*)malloc(sizeof(WebPMux));
// If mux is NULL MuxInit is a noop.
MuxInit(mux);
return mux;
}
}
static void DeleteAllChunks(WebPChunk** const chunk_list) {
while (*chunk_list) {
*chunk_list = ChunkDelete(*chunk_list);
}
}
static void MuxRelease(WebPMux* const mux) {
if (mux == NULL) return;
MuxImageDeleteAll(&mux->images_);
DeleteAllChunks(&mux->vp8x_);
DeleteAllChunks(&mux->iccp_);
DeleteAllChunks(&mux->loop_);
DeleteAllChunks(&mux->meta_);
DeleteAllChunks(&mux->unknown_);
}
void WebPMuxDelete(WebPMux* mux) {
// If mux is NULL MuxRelease is a noop.
MuxRelease(mux);
free(mux);
}
//------------------------------------------------------------------------------
// Helper method(s).
// Handy MACRO, makes MuxSet() very symmetric to MuxGet().
#define SWITCH_ID_LIST(INDEX, LIST) \
if (idx == (INDEX)) { \
err = ChunkAssignData(&chunk, data, copy_data, kChunks[(INDEX)].tag); \
if (err == WEBP_MUX_OK) { \
err = ChunkSetNth(&chunk, (LIST), nth); \
} \
return err; \
}
static WebPMuxError MuxSet(WebPMux* const mux, CHUNK_INDEX idx, uint32_t nth,
const WebPData* const data, int copy_data) {
WebPChunk chunk;
WebPMuxError err = WEBP_MUX_NOT_FOUND;
assert(mux != NULL);
assert(!IsWPI(kChunks[idx].id));
ChunkInit(&chunk);
SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x_);
SWITCH_ID_LIST(IDX_ICCP, &mux->iccp_);
SWITCH_ID_LIST(IDX_LOOP, &mux->loop_);
SWITCH_ID_LIST(IDX_META, &mux->meta_);
if (idx == IDX_UNKNOWN && data->size_ > TAG_SIZE) {
// For raw-data unknown chunk, the first four bytes should be the tag to be
// used for the chunk.
const WebPData tmp = { data->bytes_ + TAG_SIZE, data->size_ - TAG_SIZE };
err = ChunkAssignData(&chunk, &tmp, copy_data, GetLE32(data->bytes_ + 0));
if (err == WEBP_MUX_OK)
err = ChunkSetNth(&chunk, &mux->unknown_, nth);
}
return err;
}
#undef SWITCH_ID_LIST
static WebPMuxError MuxAddChunk(WebPMux* const mux, uint32_t nth, uint32_t tag,
const uint8_t* data, size_t size,
int copy_data) {
const CHUNK_INDEX idx = ChunkGetIndexFromTag(tag);
const WebPData chunk_data = { data, size };
assert(mux != NULL);
assert(size <= MAX_CHUNK_PAYLOAD);
assert(idx != IDX_NIL);
return MuxSet(mux, idx, nth, &chunk_data, copy_data);
}
// Create data for frame/tile given image data, offsets and duration.
static WebPMuxError CreateFrameTileData(const WebPData* const image,
int x_offset, int y_offset,
int duration, int is_lossless,
int is_frame,
WebPData* const frame_tile) {
int width;
int height;
uint8_t* frame_tile_bytes;
const size_t frame_tile_size = kChunks[is_frame ? IDX_FRAME : IDX_TILE].size;
const int ok = is_lossless ?
VP8LGetInfo(image->bytes_, image->size_, &width, &height, NULL) :
VP8GetInfo(image->bytes_, image->size_, image->size_, &width, &height);
if (!ok) return WEBP_MUX_INVALID_ARGUMENT;
assert(width > 0 && height > 0 && duration > 0);
// Note: assertion on upper bounds is done in PutLE24().
frame_tile_bytes = (uint8_t*)malloc(frame_tile_size);
if (frame_tile_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;
PutLE24(frame_tile_bytes + 0, x_offset / 2);
PutLE24(frame_tile_bytes + 3, y_offset / 2);
if (is_frame) {
PutLE24(frame_tile_bytes + 6, width - 1);
PutLE24(frame_tile_bytes + 9, height - 1);
PutLE24(frame_tile_bytes + 12, duration - 1);
}
frame_tile->bytes_ = frame_tile_bytes;
frame_tile->size_ = frame_tile_size;
return WEBP_MUX_OK;
}
// Outputs image data given a bitstream. The bitstream can either be a
// single-image WebP file or raw VP8/VP8L data.
// Also outputs 'is_lossless' to be true if the given bitstream is lossless.
static WebPMuxError GetImageData(const WebPData* const bitstream,
WebPData* const image, WebPData* const alpha,
int* const is_lossless) {
WebPDataInit(alpha); // Default: no alpha.
if (bitstream->size_ < TAG_SIZE ||
memcmp(bitstream->bytes_, "RIFF", TAG_SIZE)) {
// It is NOT webp file data. Return input data as is.
*image = *bitstream;
} else {
// It is webp file data. Extract image data from it.
const WebPMuxImage* wpi;
WebPMux* const mux = WebPMuxCreate(bitstream, 0);
if (mux == NULL) return WEBP_MUX_BAD_DATA;
wpi = mux->images_;
assert(wpi != NULL && wpi->img_ != NULL);
*image = wpi->img_->data_;
if (wpi->alpha_ != NULL) {
*alpha = wpi->alpha_->data_;
}
WebPMuxDelete(mux);
}
*is_lossless = VP8LCheckSignature(image->bytes_, image->size_);
return WEBP_MUX_OK;
}
static WebPMuxError DeleteChunks(WebPChunk** chunk_list, uint32_t tag) {
WebPMuxError err = WEBP_MUX_NOT_FOUND;
assert(chunk_list);
while (*chunk_list) {
WebPChunk* const chunk = *chunk_list;
if (chunk->tag_ == tag) {
*chunk_list = ChunkDelete(chunk);
err = WEBP_MUX_OK;
} else {
chunk_list = &chunk->next_;
}
}
return err;
}
static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux, CHUNK_INDEX idx) {
const WebPChunkId id = kChunks[idx].id;
WebPChunk** chunk_list;
if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
if (IsWPI(id)) return WEBP_MUX_INVALID_ARGUMENT;
chunk_list = MuxGetChunkListFromId(mux, id);
if (chunk_list == NULL) return WEBP_MUX_INVALID_ARGUMENT;
return DeleteChunks(chunk_list, kChunks[idx].tag);
}
static WebPMuxError DeleteLoopCount(WebPMux* const mux) {
return MuxDeleteAllNamedData(mux, IDX_LOOP);
}
//------------------------------------------------------------------------------
// Set API(s).
WebPMuxError WebPMuxSetImage(WebPMux* mux,
const WebPData* bitstream, int copy_data) {
WebPMuxError err;
WebPChunk chunk;
WebPMuxImage wpi;
WebPData image;
WebPData alpha;
int is_lossless;
int image_tag;
if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL ||
bitstream->size_ > MAX_CHUNK_PAYLOAD) {
return WEBP_MUX_INVALID_ARGUMENT;
}
// If given data is for a whole webp file,
// extract only the VP8/VP8L data from it.
err = GetImageData(bitstream, &image, &alpha, &is_lossless);
if (err != WEBP_MUX_OK) return err;
image_tag = is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag;
// Delete the existing images.
MuxImageDeleteAll(&mux->images_);
MuxImageInit(&wpi);
if (alpha.bytes_ != NULL) { // Add alpha chunk.
ChunkInit(&chunk);
err = ChunkAssignData(&chunk, &alpha, copy_data, kChunks[IDX_ALPHA].tag);
if (err != WEBP_MUX_OK) goto Err;
err = ChunkSetNth(&chunk, &wpi.alpha_, 1);
if (err != WEBP_MUX_OK) goto Err;
}
// Add image chunk.
ChunkInit(&chunk);
err = ChunkAssignData(&chunk, &image, copy_data, image_tag);
if (err != WEBP_MUX_OK) goto Err;
err = ChunkSetNth(&chunk, &wpi.img_, 1);
if (err != WEBP_MUX_OK) goto Err;
// Add this image to mux.
err = MuxImagePush(&wpi, &mux->images_);
if (err != WEBP_MUX_OK) goto Err;
// All OK.
return WEBP_MUX_OK;
Err:
// Something bad happened.
ChunkRelease(&chunk);
MuxImageRelease(&wpi);
return err;
}
WebPMuxError WebPMuxSetMetadata(WebPMux* mux, const WebPData* metadata,
int copy_data) {
WebPMuxError err;
if (mux == NULL || metadata == NULL || metadata->bytes_ == NULL ||
metadata->size_ > MAX_CHUNK_PAYLOAD) {
return WEBP_MUX_INVALID_ARGUMENT;
}
// Delete the existing metadata chunk(s).
err = WebPMuxDeleteMetadata(mux);
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
// Add the given metadata chunk.
return MuxSet(mux, IDX_META, 1, metadata, copy_data);
}
WebPMuxError WebPMuxSetColorProfile(WebPMux* mux, const WebPData* color_profile,
int copy_data) {
WebPMuxError err;
if (mux == NULL || color_profile == NULL || color_profile->bytes_ == NULL ||
color_profile->size_ > MAX_CHUNK_PAYLOAD) {
return WEBP_MUX_INVALID_ARGUMENT;
}
// Delete the existing ICCP chunk(s).
err = WebPMuxDeleteColorProfile(mux);
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
// Add the given ICCP chunk.
return MuxSet(mux, IDX_ICCP, 1, color_profile, copy_data);
}
WebPMuxError WebPMuxSetLoopCount(WebPMux* mux, int loop_count) {
WebPMuxError err;
uint8_t* data = NULL;
if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
if (loop_count >= MAX_LOOP_COUNT) return WEBP_MUX_INVALID_ARGUMENT;
// Delete the existing LOOP chunk(s).
err = DeleteLoopCount(mux);
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
// Add the given loop count.
data = (uint8_t*)malloc(kChunks[IDX_LOOP].size);
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
PutLE16(data, loop_count);
err = MuxAddChunk(mux, 1, kChunks[IDX_LOOP].tag, data,
kChunks[IDX_LOOP].size, 1);
free(data);
return err;
}
static WebPMuxError MuxPushFrameTileInternal(
WebPMux* const mux, const WebPData* const bitstream, int x_offset,
int y_offset, int duration, int copy_data, uint32_t tag) {
WebPChunk chunk;
WebPData image;
WebPData alpha;
WebPMuxImage wpi;
WebPMuxError err;
WebPData frame_tile;
const int is_frame = (tag == kChunks[IDX_FRAME].tag) ? 1 : 0;
int is_lossless;
int image_tag;
// Sanity checks.
if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL ||
bitstream->size_ > MAX_CHUNK_PAYLOAD) {
return WEBP_MUX_INVALID_ARGUMENT;
}
if (x_offset < 0 || x_offset >= MAX_POSITION_OFFSET ||
y_offset < 0 || y_offset >= MAX_POSITION_OFFSET ||
duration <= 0 || duration > MAX_DURATION) {
return WEBP_MUX_INVALID_ARGUMENT;
}
// Snap offsets to even positions.
x_offset &= ~1;
y_offset &= ~1;
// If given data is for a whole webp file,
// extract only the VP8/VP8L data from it.
err = GetImageData(bitstream, &image, &alpha, &is_lossless);
if (err != WEBP_MUX_OK) return err;
image_tag = is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag;
WebPDataInit(&frame_tile);
ChunkInit(&chunk);
MuxImageInit(&wpi);
if (alpha.bytes_ != NULL) {
// Add alpha chunk.
err = ChunkAssignData(&chunk, &alpha, copy_data, kChunks[IDX_ALPHA].tag);
if (err != WEBP_MUX_OK) goto Err;
err = ChunkSetNth(&chunk, &wpi.alpha_, 1);
if (err != WEBP_MUX_OK) goto Err;
ChunkInit(&chunk); // chunk owned by wpi.alpha_ now.
}
// Add image chunk.
err = ChunkAssignData(&chunk, &image, copy_data, image_tag);
if (err != WEBP_MUX_OK) goto Err;
err = ChunkSetNth(&chunk, &wpi.img_, 1);
if (err != WEBP_MUX_OK) goto Err;
ChunkInit(&chunk); // chunk owned by wpi.img_ now.
// Create frame/tile data.
err = CreateFrameTileData(&image, x_offset, y_offset, duration, is_lossless,
is_frame, &frame_tile);
if (err != WEBP_MUX_OK) goto Err;
// Add frame/tile chunk (with copy_data = 1).
err = ChunkAssignData(&chunk, &frame_tile, 1, tag);
if (err != WEBP_MUX_OK) goto Err;
WebPDataClear(&frame_tile);
err = ChunkSetNth(&chunk, &wpi.header_, 1);
if (err != WEBP_MUX_OK) goto Err;
ChunkInit(&chunk); // chunk owned by wpi.header_ now.
// Add this WebPMuxImage to mux.
err = MuxImagePush(&wpi, &mux->images_);
if (err != WEBP_MUX_OK) goto Err;
// All is well.
return WEBP_MUX_OK;
Err: // Something bad happened.
WebPDataClear(&frame_tile);
ChunkRelease(&chunk);
MuxImageRelease(&wpi);
return err;
}
WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPData* bitstream,
int x_offset, int y_offset,
int duration, int copy_data) {
return MuxPushFrameTileInternal(mux, bitstream, x_offset, y_offset,
duration, copy_data, kChunks[IDX_FRAME].tag);
}
WebPMuxError WebPMuxPushTile(WebPMux* mux, const WebPData* bitstream,
int x_offset, int y_offset,
int copy_data) {
return MuxPushFrameTileInternal(mux, bitstream, x_offset, y_offset,
1 /* unused duration */, copy_data,
kChunks[IDX_TILE].tag);
}
//------------------------------------------------------------------------------
// Delete API(s).
WebPMuxError WebPMuxDeleteImage(WebPMux* mux) {
WebPMuxError err;
if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
err = MuxValidateForImage(mux);
if (err != WEBP_MUX_OK) return err;
// All well, delete image.
MuxImageDeleteAll(&mux->images_);
return WEBP_MUX_OK;
}
WebPMuxError WebPMuxDeleteMetadata(WebPMux* mux) {
return MuxDeleteAllNamedData(mux, IDX_META);
}
WebPMuxError WebPMuxDeleteColorProfile(WebPMux* mux) {
return MuxDeleteAllNamedData(mux, IDX_ICCP);
}
static WebPMuxError DeleteFrameTileInternal(WebPMux* const mux, uint32_t nth,
CHUNK_INDEX idx) {
const WebPChunkId id = kChunks[idx].id;
if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
assert(idx == IDX_FRAME || idx == IDX_TILE);
return MuxImageDeleteNth(&mux->images_, nth, id);
}
WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) {
return DeleteFrameTileInternal(mux, nth, IDX_FRAME);
}
WebPMuxError WebPMuxDeleteTile(WebPMux* mux, uint32_t nth) {
return DeleteFrameTileInternal(mux, nth, IDX_TILE);
}
//------------------------------------------------------------------------------
// Assembly of the WebP RIFF file.
static WebPMuxError GetFrameTileInfo(const WebPChunk* const frame_tile_chunk,
int* const x_offset, int* const y_offset,
int* const duration) {
const uint32_t tag = frame_tile_chunk->tag_;
const int is_frame = (tag == kChunks[IDX_FRAME].tag);
const WebPData* const data = &frame_tile_chunk->data_;
const size_t expected_data_size =
is_frame ? FRAME_CHUNK_SIZE : TILE_CHUNK_SIZE;
assert(frame_tile_chunk != NULL);
assert(tag == kChunks[IDX_FRAME].tag || tag == kChunks[IDX_TILE].tag);
if (data->size_ != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT;
*x_offset = 2 * GetLE24(data->bytes_ + 0);
*y_offset = 2 * GetLE24(data->bytes_ + 3);
if (is_frame) *duration = 1 + GetLE24(data->bytes_ + 12);
return WEBP_MUX_OK;
}
WebPMuxError MuxGetImageWidthHeight(const WebPChunk* const image_chunk,
int* const width, int* const height) {
const uint32_t tag = image_chunk->tag_;
const WebPData* const data = &image_chunk->data_;
int w, h;
int ok;
assert(image_chunk != NULL);
assert(tag == kChunks[IDX_VP8].tag || tag == kChunks[IDX_VP8L].tag);
ok = (tag == kChunks[IDX_VP8].tag) ?
VP8GetInfo(data->bytes_, data->size_, data->size_, &w, &h) :
VP8LGetInfo(data->bytes_, data->size_, &w, &h, NULL);
if (ok) {
*width = w;
*height = h;
return WEBP_MUX_OK;
} else {
return WEBP_MUX_BAD_DATA;
}
}
static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
int* const x_offset, int* const y_offset,
int* const duration,
int* const width, int* const height) {
const WebPChunk* const image_chunk = wpi->img_;
const WebPChunk* const frame_tile_chunk = wpi->header_;
// Get offsets and duration from FRM/TILE chunk.
const WebPMuxError err =
GetFrameTileInfo(frame_tile_chunk, x_offset, y_offset, duration);
if (err != WEBP_MUX_OK) return err;
// Get width and height from VP8/VP8L chunk.
return MuxGetImageWidthHeight(image_chunk, width, height);
}
static WebPMuxError GetImageCanvasWidthHeight(
const WebPMux* const mux, uint32_t flags,
int* const width, int* const height) {
WebPMuxImage* wpi = NULL;
assert(mux != NULL);
assert(width != NULL && height != NULL);
wpi = mux->images_;
assert(wpi != NULL);
assert(wpi->img_ != NULL);
if (wpi->next_) {
int max_x = 0;
int max_y = 0;
int64_t image_area = 0;
// Aggregate the bounding box for animation frames & tiled images.
for (; wpi != NULL; wpi = wpi->next_) {
int x_offset, y_offset, duration, w, h;
const WebPMuxError err = GetImageInfo(wpi, &x_offset, &y_offset,
&duration, &w, &h);
const int max_x_pos = x_offset + w;
const int max_y_pos = y_offset + h;
if (err != WEBP_MUX_OK) return err;
assert(x_offset < MAX_POSITION_OFFSET);
assert(y_offset < MAX_POSITION_OFFSET);
if (max_x_pos > max_x) max_x = max_x_pos;
if (max_y_pos > max_y) max_y = max_y_pos;
image_area += w * h;
}
*width = max_x;
*height = max_y;
// Crude check to validate that there are no image overlaps/holes for tile
// images. Check that the aggregated image area for individual tiles exactly
// matches the image area of the constructed canvas. However, the area-match
// is necessary but not sufficient condition.
if ((flags & TILE_FLAG) && (image_area != (max_x * max_y))) {
*width = 0;
*height = 0;
return WEBP_MUX_INVALID_ARGUMENT;
}
} else {
// For a single image, extract the width & height from VP8/VP8L image-data.
int w, h;
const WebPChunk* const image_chunk = wpi->img_;
const WebPMuxError err = MuxGetImageWidthHeight(image_chunk, &w, &h);
if (err != WEBP_MUX_OK) return err;
*width = w;
*height = h;
}
return WEBP_MUX_OK;
}
// VP8X format:
// Total Size : 10,
// Flags : 4 bytes,
// Width : 3 bytes,
// Height : 3 bytes.
static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
WebPMuxError err = WEBP_MUX_OK;
uint32_t flags = 0;
int width = 0;
int height = 0;
uint8_t data[VP8X_CHUNK_SIZE];
const size_t data_size = VP8X_CHUNK_SIZE;
const WebPMuxImage* images = NULL;
assert(mux != NULL);
images = mux->images_; // First image.
if (images == NULL || images->img_ == NULL ||
images->img_->data_.bytes_ == NULL) {
return WEBP_MUX_INVALID_ARGUMENT;
}
// If VP8X chunk(s) is(are) already present, remove them (and later add new
// VP8X chunk with updated flags).
err = MuxDeleteAllNamedData(mux, IDX_VP8X);
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
// Set flags.
if (mux->iccp_ != NULL && mux->iccp_->data_.bytes_ != NULL) {
flags |= ICCP_FLAG;
}
if (mux->meta_ != NULL && mux->meta_->data_.bytes_ != NULL) {
flags |= META_FLAG;
}
if (images->header_ != NULL) {
if (images->header_->tag_ == kChunks[IDX_TILE].tag) {
// This is a tiled image.
flags |= TILE_FLAG;
} else if (images->header_->tag_ == kChunks[IDX_FRAME].tag) {
// This is an image with animation.
flags |= ANIMATION_FLAG;
}
}
if (MuxImageCount(images, WEBP_CHUNK_ALPHA) > 0) {
flags |= ALPHA_FLAG; // Some images have an alpha channel.
}
if (flags == 0) {
// For Simple Image, VP8X chunk should not be added.
return WEBP_MUX_OK;
}
err = GetImageCanvasWidthHeight(mux, flags, &width, &height);
if (err != WEBP_MUX_OK) return err;
if (width <= 0 || height <= 0) {
return WEBP_MUX_INVALID_ARGUMENT;
}
if (width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) {
return WEBP_MUX_INVALID_ARGUMENT;
}
if (MuxHasLosslessImages(images)) {
// We have a file with a VP8X chunk having some lossless images.
// As lossless images implicitly contain alpha, force ALPHA_FLAG to be true.
// Note: This 'flags' update must NOT be done for a lossless image
// without a VP8X chunk!
flags |= ALPHA_FLAG;
}
PutLE32(data + 0, flags); // VP8X chunk flags.
PutLE24(data + 4, width - 1); // canvas width.
PutLE24(data + 7, height - 1); // canvas height.
err = MuxAddChunk(mux, 1, kChunks[IDX_VP8X].tag, data, data_size, 1);
return err;
}
WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
size_t size = 0;
uint8_t* data = NULL;
uint8_t* dst = NULL;
int num_frames;
int num_loop_chunks;
WebPMuxError err;
if (mux == NULL || assembled_data == NULL) {
return WEBP_MUX_INVALID_ARGUMENT;
}
// Remove LOOP chunk if unnecessary.
err = WebPMuxNumChunks(mux, kChunks[IDX_LOOP].id, &num_loop_chunks);
if (err != WEBP_MUX_OK) return err;
if (num_loop_chunks >= 1) {
err = WebPMuxNumChunks(mux, kChunks[IDX_FRAME].id, &num_frames);
if (err != WEBP_MUX_OK) return err;
if (num_frames == 0) {
err = DeleteLoopCount(mux);
if (err != WEBP_MUX_OK) return err;
}
}
// Create VP8X chunk.
err = CreateVP8XChunk(mux);
if (err != WEBP_MUX_OK) return err;
// Allocate data.
size = ChunksListDiskSize(mux->vp8x_) + ChunksListDiskSize(mux->iccp_)
+ ChunksListDiskSize(mux->loop_) + MuxImageListDiskSize(mux->images_)
+ ChunksListDiskSize(mux->meta_) + ChunksListDiskSize(mux->unknown_)
+ RIFF_HEADER_SIZE;
data = (uint8_t*)malloc(size);
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
// Emit header & chunks.
dst = MuxEmitRiffHeader(data, size);
dst = ChunkListEmit(mux->vp8x_, dst);
dst = ChunkListEmit(mux->iccp_, dst);
dst = ChunkListEmit(mux->loop_, dst);
dst = MuxImageListEmit(mux->images_, dst);
dst = ChunkListEmit(mux->meta_, dst);
dst = ChunkListEmit(mux->unknown_, dst);
assert(dst == data + size);
// Validate mux.
err = MuxValidate(mux);
if (err != WEBP_MUX_OK) {
free(data);
data = NULL;
size = 0;
}
// Finalize.
assembled_data->bytes_ = data;
assembled_data->size_ = size;
return err;
}
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

271
src/mux/muxi.h Normal file
View File

@ -0,0 +1,271 @@
// Copyright 2011 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Internal header for mux library.
//
// Author: Urvang (urvang@google.com)
#ifndef WEBP_MUX_MUXI_H_
#define WEBP_MUX_MUXI_H_
#include <stdlib.h>
#include "../dec/vp8i.h"
#include "../dec/vp8li.h"
#include "../webp/format_constants.h"
#include "../webp/mux.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------
// Defines and constants.
// Chunk object.
typedef struct WebPChunk WebPChunk;
struct WebPChunk {
uint32_t tag_;
int owner_; // True if *data_ memory is owned internally.
// VP8X, Loop, and other internally created chunks
// like frame/tile are always owned.
WebPData data_;
WebPChunk* next_;
};
// MuxImage object. Store a full webp image (including frame/tile chunk, alpha
// chunk and VP8/VP8L chunk),
typedef struct WebPMuxImage WebPMuxImage;
struct WebPMuxImage {
WebPChunk* header_; // Corresponds to WEBP_CHUNK_FRAME/WEBP_CHUNK_TILE.
WebPChunk* alpha_; // Corresponds to WEBP_CHUNK_ALPHA.
WebPChunk* img_; // Corresponds to WEBP_CHUNK_IMAGE.
int is_partial_; // True if only some of the chunks are filled.
WebPMuxImage* next_;
};
// Main mux object. Stores data chunks.
struct WebPMux {
WebPMuxImage* images_;
WebPChunk* iccp_;
WebPChunk* meta_;
WebPChunk* loop_;
WebPChunk* vp8x_;
WebPChunk* unknown_;
};
// CHUNK_INDEX enum: used for indexing within 'kChunks' (defined below) only.
// Note: the reason for having two enums ('WebPChunkId' and 'CHUNK_INDEX') is to
// allow two different chunks to have the same id (e.g. WebPChunkId
// 'WEBP_CHUNK_IMAGE' can correspond to CHUNK_INDEX 'IDX_VP8' or 'IDX_VP8L').
typedef enum {
IDX_VP8X = 0,
IDX_ICCP,
IDX_LOOP,
IDX_FRAME,
IDX_TILE,
IDX_ALPHA,
IDX_VP8,
IDX_VP8L,
IDX_META,
IDX_UNKNOWN,
IDX_NIL,
IDX_LAST_CHUNK
} CHUNK_INDEX;
#define NIL_TAG 0x00000000u // To signal void chunk.
#define MKFOURCC(a, b, c, d) ((uint32_t)(a) | (b) << 8 | (c) << 16 | (d) << 24)
typedef struct {
uint32_t tag;
WebPChunkId id;
uint32_t size;
} ChunkInfo;
extern const ChunkInfo kChunks[IDX_LAST_CHUNK];
//------------------------------------------------------------------------------
// Helper functions.
// Read 16, 24 or 32 bits stored in little-endian order.
static WEBP_INLINE int GetLE16(const uint8_t* const data) {
return (int)(data[0] << 0) | (data[1] << 8);
}
static WEBP_INLINE int GetLE24(const uint8_t* const data) {
return GetLE16(data) | (data[2] << 16);
}
static WEBP_INLINE uint32_t GetLE32(const uint8_t* const data) {
return (uint32_t)GetLE16(data) | (GetLE16(data + 2) << 16);
}
// Store 16, 24 or 32 bits in little-endian order.
static WEBP_INLINE void PutLE16(uint8_t* const data, int val) {
assert(val < (1 << 16));
data[0] = (val >> 0);
data[1] = (val >> 8);
}
static WEBP_INLINE void PutLE24(uint8_t* const data, int val) {
assert(val < (1 << 24));
PutLE16(data, val & 0xffff);
data[2] = (val >> 16);
}
static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
PutLE16(data, (int)(val & 0xffff));
PutLE16(data + 2, (int)(val >> 16));
}
static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) {
return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U);
}
//------------------------------------------------------------------------------
// Chunk object management.
// Initialize.
void ChunkInit(WebPChunk* const chunk);
// Get chunk index from chunk tag. Returns IDX_NIL if not found.
CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag);
// Get chunk id from chunk tag. Returns WEBP_CHUNK_NIL if not found.
WebPChunkId ChunkGetIdFromTag(uint32_t tag);
// Search for nth chunk with given 'tag' in the chunk list.
// nth = 0 means "last of the list".
WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag);
// Fill the chunk with the given data.
WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
int copy_data, uint32_t tag);
// Sets 'chunk' at nth position in the 'chunk_list'.
// nth = 0 has the special meaning "last of the list".
WebPMuxError ChunkSetNth(const WebPChunk* chunk, WebPChunk** chunk_list,
uint32_t nth);
// Releases chunk and returns chunk->next_.
WebPChunk* ChunkRelease(WebPChunk* const chunk);
// Deletes given chunk & returns chunk->next_.
WebPChunk* ChunkDelete(WebPChunk* const chunk);
// Size of a chunk including header and padding.
static WEBP_INLINE size_t ChunkDiskSize(const WebPChunk* chunk) {
const size_t data_size = chunk->data_.size_;
assert(data_size < MAX_CHUNK_PAYLOAD);
return SizeWithPadding(data_size);
}
// Total size of a list of chunks.
size_t ChunksListDiskSize(const WebPChunk* chunk_list);
// Write out the given list of chunks into 'dst'.
uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst);
// Get the width & height of image stored in 'image_chunk'.
WebPMuxError MuxGetImageWidthHeight(const WebPChunk* const image_chunk,
int* const width, int* const height);
//------------------------------------------------------------------------------
// MuxImage object management.
// Initialize.
void MuxImageInit(WebPMuxImage* const wpi);
// Releases image 'wpi' and returns wpi->next.
WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi);
// Delete image 'wpi' and return the next image in the list or NULL.
// 'wpi' can be NULL.
WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi);
// Delete all images in 'wpi_list'.
void MuxImageDeleteAll(WebPMuxImage** const wpi_list);
// Count number of images matching the given tag id in the 'wpi_list'.
int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id);
// Check if given ID corresponds to an image related chunk.
static WEBP_INLINE int IsWPI(WebPChunkId id) {
switch (id) {
case WEBP_CHUNK_FRAME:
case WEBP_CHUNK_TILE:
case WEBP_CHUNK_ALPHA:
case WEBP_CHUNK_IMAGE: return 1;
default: return 0;
}
}
// Get a reference to appropriate chunk list within an image given chunk tag.
static WEBP_INLINE WebPChunk** MuxImageGetListFromId(
const WebPMuxImage* const wpi, WebPChunkId id) {
assert(wpi != NULL);
switch (id) {
case WEBP_CHUNK_FRAME:
case WEBP_CHUNK_TILE: return (WebPChunk**)&wpi->header_;
case WEBP_CHUNK_ALPHA: return (WebPChunk**)&wpi->alpha_;
case WEBP_CHUNK_IMAGE: return (WebPChunk**)&wpi->img_;
default: return NULL;
}
}
// Pushes 'wpi' at the end of 'wpi_list'.
WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list);
// Delete nth image in the image list with given tag id.
WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth,
WebPChunkId id);
// Get nth image in the image list with given tag id.
WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth,
WebPChunkId id, WebPMuxImage** wpi);
// Total size of the given image.
size_t MuxImageDiskSize(const WebPMuxImage* const wpi);
// Total size of a list of images.
size_t MuxImageListDiskSize(const WebPMuxImage* wpi_list);
// Write out the given image into 'dst'.
uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst);
// Write out the given list of images into 'dst'.
uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst);
//------------------------------------------------------------------------------
// Helper methods for mux.
// Checks if the given image list contains at least one lossless image.
int MuxHasLosslessImages(const WebPMuxImage* images);
// Write out RIFF header into 'data', given total data size 'size'.
uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size);
// Returns the list where chunk with given ID is to be inserted in mux.
// Return value is NULL if this chunk should be inserted in mux->images_ list
// or if 'id' is not known.
WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id);
// Validates that the given mux has a single image.
WebPMuxError MuxValidateForImage(const WebPMux* const mux);
// Validates the given mux object.
WebPMuxError MuxValidate(const WebPMux* const mux);
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
#endif /* WEBP_MUX_MUXI_H_ */

576
src/mux/muxinternal.c Normal file
View File

@ -0,0 +1,576 @@
// Copyright 2011 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Internal objects and utils for mux.
//
// Authors: Urvang (urvang@google.com)
// Vikas (vikasa@google.com)
#include <assert.h>
#include "./muxi.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#define UNDEFINED_CHUNK_SIZE (-1)
const ChunkInfo kChunks[] = {
{ MKFOURCC('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE },
{ MKFOURCC('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE },
{ MKFOURCC('L', 'O', 'O', 'P'), WEBP_CHUNK_LOOP, LOOP_CHUNK_SIZE },
{ MKFOURCC('F', 'R', 'M', ' '), WEBP_CHUNK_FRAME, FRAME_CHUNK_SIZE },
{ MKFOURCC('T', 'I', 'L', 'E'), WEBP_CHUNK_TILE, TILE_CHUNK_SIZE },
{ MKFOURCC('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE },
{ MKFOURCC('V', 'P', '8', ' '), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE },
{ MKFOURCC('V', 'P', '8', 'L'), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE },
{ MKFOURCC('M', 'E', 'T', 'A'), WEBP_CHUNK_META, UNDEFINED_CHUNK_SIZE },
{ MKFOURCC('U', 'N', 'K', 'N'), WEBP_CHUNK_UNKNOWN, UNDEFINED_CHUNK_SIZE },
{ NIL_TAG, WEBP_CHUNK_NIL, UNDEFINED_CHUNK_SIZE }
};
//------------------------------------------------------------------------------
// Life of a chunk object.
void ChunkInit(WebPChunk* const chunk) {
assert(chunk);
memset(chunk, 0, sizeof(*chunk));
chunk->tag_ = NIL_TAG;
}
WebPChunk* ChunkRelease(WebPChunk* const chunk) {
WebPChunk* next;
if (chunk == NULL) return NULL;
if (chunk->owner_) {
WebPDataClear(&chunk->data_);
}
next = chunk->next_;
ChunkInit(chunk);
return next;
}
//------------------------------------------------------------------------------
// Chunk misc methods.
CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag) {
int i;
for (i = 0; kChunks[i].tag != NIL_TAG; ++i) {
if (tag == kChunks[i].tag) return i;
}
return IDX_NIL;
}
WebPChunkId ChunkGetIdFromTag(uint32_t tag) {
int i;
for (i = 0; kChunks[i].tag != NIL_TAG; ++i) {
if (tag == kChunks[i].tag) return kChunks[i].id;
}
return WEBP_CHUNK_NIL;
}
//------------------------------------------------------------------------------
// Chunk search methods.
// Returns next chunk in the chunk list with the given tag.
static WebPChunk* ChunkSearchNextInList(WebPChunk* chunk, uint32_t tag) {
while (chunk && chunk->tag_ != tag) {
chunk = chunk->next_;
}
return chunk;
}
WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) {
uint32_t iter = nth;
first = ChunkSearchNextInList(first, tag);
if (!first) return NULL;
while (--iter != 0) {
WebPChunk* next_chunk = ChunkSearchNextInList(first->next_, tag);
if (next_chunk == NULL) break;
first = next_chunk;
}
return ((nth > 0) && (iter > 0)) ? NULL : first;
}
// Outputs a pointer to 'prev_chunk->next_',
// where 'prev_chunk' is the pointer to the chunk at position (nth - 1).
// Returns 1 if nth chunk was found, 0 otherwise.
static int ChunkSearchListToSet(WebPChunk** chunk_list, uint32_t nth,
WebPChunk*** const location) {
uint32_t count = 0;
assert(chunk_list);
*location = chunk_list;
while (*chunk_list) {
WebPChunk* const cur_chunk = *chunk_list;
++count;
if (count == nth) return 1; // Found.
chunk_list = &cur_chunk->next_;
*location = chunk_list;
}
// *chunk_list is ok to be NULL if adding at last location.
return (nth == 0 || (count == nth - 1)) ? 1 : 0;
}
//------------------------------------------------------------------------------
// Chunk writer methods.
WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
int copy_data, uint32_t tag) {
// For internally allocated chunks, always copy data & make it owner of data.
if (tag == kChunks[IDX_VP8X].tag || tag == kChunks[IDX_LOOP].tag) {
copy_data = 1;
}
ChunkRelease(chunk);
if (data != NULL) {
if (copy_data) {
// Copy data.
chunk->data_.bytes_ = (uint8_t*)malloc(data->size_);
if (chunk->data_.bytes_ == NULL) return WEBP_MUX_MEMORY_ERROR;
memcpy((uint8_t*)chunk->data_.bytes_, data->bytes_, data->size_);
chunk->data_.size_ = data->size_;
// Chunk is owner of data.
chunk->owner_ = 1;
} else {
// Don't copy data.
chunk->data_ = *data;
}
}
chunk->tag_ = tag;
return WEBP_MUX_OK;
}
WebPMuxError ChunkSetNth(const WebPChunk* chunk, WebPChunk** chunk_list,
uint32_t nth) {
WebPChunk* new_chunk;
if (!ChunkSearchListToSet(chunk_list, nth, &chunk_list)) {
return WEBP_MUX_NOT_FOUND;
}
new_chunk = (WebPChunk*)malloc(sizeof(*new_chunk));
if (new_chunk == NULL) return WEBP_MUX_MEMORY_ERROR;
*new_chunk = *chunk;
new_chunk->next_ = *chunk_list;
*chunk_list = new_chunk;
return WEBP_MUX_OK;
}
//------------------------------------------------------------------------------
// Chunk deletion method(s).
WebPChunk* ChunkDelete(WebPChunk* const chunk) {
WebPChunk* const next = ChunkRelease(chunk);
free(chunk);
return next;
}
//------------------------------------------------------------------------------
// Chunk serialization methods.
size_t ChunksListDiskSize(const WebPChunk* chunk_list) {
size_t size = 0;
while (chunk_list) {
size += ChunkDiskSize(chunk_list);
chunk_list = chunk_list->next_;
}
return size;
}
static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) {
const size_t chunk_size = chunk->data_.size_;
assert(chunk);
assert(chunk->tag_ != NIL_TAG);
PutLE32(dst + 0, chunk->tag_);
PutLE32(dst + TAG_SIZE, (uint32_t)chunk_size);
assert(chunk_size == (uint32_t)chunk_size);
memcpy(dst + CHUNK_HEADER_SIZE, chunk->data_.bytes_, chunk_size);
if (chunk_size & 1)
dst[CHUNK_HEADER_SIZE + chunk_size] = 0; // Add padding.
return dst + ChunkDiskSize(chunk);
}
uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst) {
while (chunk_list) {
dst = ChunkEmit(chunk_list, dst);
chunk_list = chunk_list->next_;
}
return dst;
}
//------------------------------------------------------------------------------
// Manipulation of a WebPData object.
void WebPDataInit(WebPData* webp_data) {
if (webp_data != NULL) {
memset(webp_data, 0, sizeof(*webp_data));
}
}
void WebPDataClear(WebPData* webp_data) {
if (webp_data != NULL) {
free((void*)webp_data->bytes_);
WebPDataInit(webp_data);
}
}
int WebPDataCopy(const WebPData* src, WebPData* dst) {
if (src == NULL || dst == NULL) return 0;
WebPDataInit(dst);
if (src->bytes_ != NULL && src->size_ != 0) {
dst->bytes_ = (uint8_t*)malloc(src->size_);
if (dst->bytes_ == NULL) return 0;
memcpy((void*)dst->bytes_, src->bytes_, src->size_);
dst->size_ = src->size_;
}
return 1;
}
//------------------------------------------------------------------------------
// Life of a MuxImage object.
void MuxImageInit(WebPMuxImage* const wpi) {
assert(wpi);
memset(wpi, 0, sizeof(*wpi));
}
WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) {
WebPMuxImage* next;
if (wpi == NULL) return NULL;
ChunkDelete(wpi->header_);
ChunkDelete(wpi->alpha_);
ChunkDelete(wpi->img_);
next = wpi->next_;
MuxImageInit(wpi);
return next;
}
//------------------------------------------------------------------------------
// MuxImage search methods.
int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id) {
int count = 0;
const WebPMuxImage* current;
for (current = wpi_list; current != NULL; current = current->next_) {
const WebPChunk* const wpi_chunk = *MuxImageGetListFromId(current, id);
if (wpi_chunk != NULL) {
const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_);
if (wpi_chunk_id == id) ++count;
}
}
return count;
}
// Outputs a pointer to 'prev_wpi->next_',
// where 'prev_wpi' is the pointer to the image at position (nth - 1).
// Returns 1 if nth image with given id was found, 0 otherwise.
static int SearchImageToGetOrDelete(WebPMuxImage** wpi_list, uint32_t nth,
WebPChunkId id,
WebPMuxImage*** const location) {
uint32_t count = 0;
assert(wpi_list);
*location = wpi_list;
// Search makes sense only for the following.
assert(id == WEBP_CHUNK_FRAME || id == WEBP_CHUNK_TILE ||
id == WEBP_CHUNK_IMAGE);
assert(id != WEBP_CHUNK_IMAGE || nth == 1);
if (nth == 0) {
nth = MuxImageCount(*wpi_list, id);
if (nth == 0) return 0; // Not found.
}
while (*wpi_list) {
WebPMuxImage* const cur_wpi = *wpi_list;
const WebPChunk* const wpi_chunk = *MuxImageGetListFromId(cur_wpi, id);
if (wpi_chunk != NULL) {
const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_);
if (wpi_chunk_id == id) {
++count;
if (count == nth) return 1; // Found.
}
}
wpi_list = &cur_wpi->next_;
*location = wpi_list;
}
return 0; // Not found.
}
//------------------------------------------------------------------------------
// MuxImage writer methods.
WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list) {
WebPMuxImage* new_wpi;
while (*wpi_list != NULL) {
WebPMuxImage* const cur_wpi = *wpi_list;
if (cur_wpi->next_ == NULL) break;
wpi_list = &cur_wpi->next_;
}
new_wpi = (WebPMuxImage*)malloc(sizeof(*new_wpi));
if (new_wpi == NULL) return WEBP_MUX_MEMORY_ERROR;
*new_wpi = *wpi;
new_wpi->next_ = NULL;
if (*wpi_list != NULL) {
(*wpi_list)->next_ = new_wpi;
} else {
*wpi_list = new_wpi;
}
return WEBP_MUX_OK;
}
//------------------------------------------------------------------------------
// MuxImage deletion methods.
WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi) {
// Delete the components of wpi. If wpi is NULL this is a noop.
WebPMuxImage* const next = MuxImageRelease(wpi);
free(wpi);
return next;
}
void MuxImageDeleteAll(WebPMuxImage** const wpi_list) {
while (*wpi_list) {
*wpi_list = MuxImageDelete(*wpi_list);
}
}
WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth,
WebPChunkId id) {
assert(wpi_list);
if (!SearchImageToGetOrDelete(wpi_list, nth, id, &wpi_list)) {
return WEBP_MUX_NOT_FOUND;
}
*wpi_list = MuxImageDelete(*wpi_list);
return WEBP_MUX_OK;
}
//------------------------------------------------------------------------------
// MuxImage reader methods.
WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth,
WebPChunkId id, WebPMuxImage** wpi) {
assert(wpi_list);
assert(wpi);
if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth, id,
(WebPMuxImage***)&wpi_list)) {
return WEBP_MUX_NOT_FOUND;
}
*wpi = (WebPMuxImage*)*wpi_list;
return WEBP_MUX_OK;
}
//------------------------------------------------------------------------------
// MuxImage serialization methods.
// Size of an image.
size_t MuxImageDiskSize(const WebPMuxImage* const wpi) {
size_t size = 0;
if (wpi->header_ != NULL) size += ChunkDiskSize(wpi->header_);
if (wpi->alpha_ != NULL) size += ChunkDiskSize(wpi->alpha_);
if (wpi->img_ != NULL) size += ChunkDiskSize(wpi->img_);
return size;
}
size_t MuxImageListDiskSize(const WebPMuxImage* wpi_list) {
size_t size = 0;
while (wpi_list) {
size += MuxImageDiskSize(wpi_list);
wpi_list = wpi_list->next_;
}
return size;
}
uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) {
// Ordering of chunks to be emitted is strictly as follows:
// 1. Frame/Tile chunk (if present).
// 2. Alpha chunk (if present).
// 3. VP8/VP8L chunk.
assert(wpi);
if (wpi->header_ != NULL) dst = ChunkEmit(wpi->header_, dst);
if (wpi->alpha_ != NULL) dst = ChunkEmit(wpi->alpha_, dst);
if (wpi->img_ != NULL) dst = ChunkEmit(wpi->img_, dst);
return dst;
}
uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst) {
while (wpi_list) {
dst = MuxImageEmit(wpi_list, dst);
wpi_list = wpi_list->next_;
}
return dst;
}
//------------------------------------------------------------------------------
// Helper methods for mux.
int MuxHasLosslessImages(const WebPMuxImage* images) {
while (images != NULL) {
assert(images->img_ != NULL);
if (images->img_->tag_ == kChunks[IDX_VP8L].tag) {
return 1;
}
images = images->next_;
}
return 0;
}
uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size) {
PutLE32(data + 0, MKFOURCC('R', 'I', 'F', 'F'));
PutLE32(data + TAG_SIZE, (uint32_t)size - CHUNK_HEADER_SIZE);
assert(size == (uint32_t)size);
PutLE32(data + TAG_SIZE + CHUNK_SIZE_BYTES, MKFOURCC('W', 'E', 'B', 'P'));
return data + RIFF_HEADER_SIZE;
}
WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id) {
assert(mux != NULL);
switch(id) {
case WEBP_CHUNK_VP8X: return (WebPChunk**)&mux->vp8x_;
case WEBP_CHUNK_ICCP: return (WebPChunk**)&mux->iccp_;
case WEBP_CHUNK_LOOP: return (WebPChunk**)&mux->loop_;
case WEBP_CHUNK_META: return (WebPChunk**)&mux->meta_;
case WEBP_CHUNK_UNKNOWN: return (WebPChunk**)&mux->unknown_;
default: return NULL;
}
}
WebPMuxError MuxValidateForImage(const WebPMux* const mux) {
const int num_images = MuxImageCount(mux->images_, WEBP_CHUNK_IMAGE);
const int num_frames = MuxImageCount(mux->images_, WEBP_CHUNK_FRAME);
const int num_tiles = MuxImageCount(mux->images_, WEBP_CHUNK_TILE);
if (num_images == 0) {
// No images in mux.
return WEBP_MUX_NOT_FOUND;
} else if (num_images == 1 && num_frames == 0 && num_tiles == 0) {
// Valid case (single image).
return WEBP_MUX_OK;
} else {
// Frame/Tile case OR an invalid mux.
return WEBP_MUX_INVALID_ARGUMENT;
}
}
static int IsNotCompatible(int feature, int num_items) {
return (feature != 0) != (num_items > 0);
}
#define NO_FLAG 0
// Test basic constraints:
// retrieval, maximum number of chunks by index (use -1 to skip)
// and feature incompatibility (use NO_FLAG to skip).
// On success returns WEBP_MUX_OK and stores the chunk count in *num.
static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx,
WebPFeatureFlags feature,
WebPFeatureFlags vp8x_flags,
int max, int* num) {
const WebPMuxError err =
WebPMuxNumChunks(mux, kChunks[idx].id, num);
if (err != WEBP_MUX_OK) return err;
if (max > -1 && *num > max) return WEBP_MUX_INVALID_ARGUMENT;
if (feature != NO_FLAG && IsNotCompatible(vp8x_flags & feature, *num)) {
return WEBP_MUX_INVALID_ARGUMENT;
}
return WEBP_MUX_OK;
}
WebPMuxError MuxValidate(const WebPMux* const mux) {
int num_iccp;
int num_meta;
int num_loop_chunks;
int num_frames;
int num_tiles;
int num_vp8x;
int num_images;
int num_alpha;
uint32_t flags;
WebPMuxError err;
// Verify mux is not NULL.
if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
// Verify mux has at least one image.
if (mux->images_ == NULL) return WEBP_MUX_INVALID_ARGUMENT;
err = WebPMuxGetFeatures(mux, &flags);
if (err != WEBP_MUX_OK) return err;
// At most one color profile chunk.
err = ValidateChunk(mux, IDX_ICCP, ICCP_FLAG, flags, 1, &num_iccp);
if (err != WEBP_MUX_OK) return err;
// At most one XMP metadata.
err = ValidateChunk(mux, IDX_META, META_FLAG, flags, 1, &num_meta);
if (err != WEBP_MUX_OK) return err;
// Animation: ANIMATION_FLAG, loop chunk and frame chunk(s) are consistent.
// At most one loop chunk.
err = ValidateChunk(mux, IDX_LOOP, NO_FLAG, flags, 1, &num_loop_chunks);
if (err != WEBP_MUX_OK) return err;
err = ValidateChunk(mux, IDX_FRAME, NO_FLAG, flags, -1, &num_frames);
if (err != WEBP_MUX_OK) return err;
{
const int has_animation = !!(flags & ANIMATION_FLAG);
if (has_animation && (num_loop_chunks == 0 || num_frames == 0)) {
return WEBP_MUX_INVALID_ARGUMENT;
}
if (!has_animation && (num_loop_chunks == 1 || num_frames > 0)) {
return WEBP_MUX_INVALID_ARGUMENT;
}
}
// Tiling: TILE_FLAG and tile chunk(s) are consistent.
err = ValidateChunk(mux, IDX_TILE, TILE_FLAG, flags, -1, &num_tiles);
if (err != WEBP_MUX_OK) return err;
// Verify either VP8X chunk is present OR there is only one elem in
// mux->images_.
err = ValidateChunk(mux, IDX_VP8X, NO_FLAG, flags, 1, &num_vp8x);
if (err != WEBP_MUX_OK) return err;
err = ValidateChunk(mux, IDX_VP8, NO_FLAG, flags, -1, &num_images);
if (err != WEBP_MUX_OK) return err;
if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT;
// ALPHA_FLAG & alpha chunk(s) are consistent.
if (num_vp8x > 0 && MuxHasLosslessImages(mux->images_)) {
// Special case: we have a VP8X chunk as well as some lossless images.
if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT;
} else {
err = ValidateChunk(mux, IDX_ALPHA, ALPHA_FLAG, flags, -1, &num_alpha);
if (err != WEBP_MUX_OK) return err;
}
// num_tiles & num_images are consistent.
if (num_tiles > 0 && num_images != num_tiles) {
return WEBP_MUX_INVALID_ARGUMENT;
}
return WEBP_MUX_OK;
}
#undef NO_FLAG
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

411
src/mux/muxread.c Normal file
View File

@ -0,0 +1,411 @@
// Copyright 2011 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Read APIs for mux.
//
// Authors: Urvang (urvang@google.com)
// Vikas (vikasa@google.com)
#include <assert.h>
#include "./muxi.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------
// Helper method(s).
// Handy MACRO.
#define SWITCH_ID_LIST(INDEX, LIST) \
if (idx == (INDEX)) { \
const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \
kChunks[(INDEX)].tag); \
if (chunk) { \
*data = chunk->data_; \
return WEBP_MUX_OK; \
} else { \
return WEBP_MUX_NOT_FOUND; \
} \
}
static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
uint32_t nth, WebPData* const data) {
assert(mux != NULL);
assert(!IsWPI(kChunks[idx].id));
WebPDataInit(data);
SWITCH_ID_LIST(IDX_VP8X, mux->vp8x_);
SWITCH_ID_LIST(IDX_ICCP, mux->iccp_);
SWITCH_ID_LIST(IDX_LOOP, mux->loop_);
SWITCH_ID_LIST(IDX_META, mux->meta_);
SWITCH_ID_LIST(IDX_UNKNOWN, mux->unknown_);
return WEBP_MUX_NOT_FOUND;
}
#undef SWITCH_ID_LIST
// Fill the chunk with the given data (includes chunk header bytes), after some
// verifications.
static WebPMuxError ChunkVerifyAndAssignData(WebPChunk* chunk,
const uint8_t* data,
size_t data_size, size_t riff_size,
int copy_data) {
uint32_t chunk_size;
WebPData chunk_data;
// Sanity checks.
if (data_size < TAG_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA;
chunk_size = GetLE32(data + TAG_SIZE);
{
const size_t chunk_disk_size = SizeWithPadding(chunk_size);
if (chunk_disk_size > riff_size) return WEBP_MUX_BAD_DATA;
if (chunk_disk_size > data_size) return WEBP_MUX_NOT_ENOUGH_DATA;
}
// Data assignment.
chunk_data.bytes_ = data + CHUNK_HEADER_SIZE;
chunk_data.size_ = chunk_size;
return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0));
}
//------------------------------------------------------------------------------
// Create a mux object from WebP-RIFF data.
WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
int version) {
size_t riff_size;
uint32_t tag;
const uint8_t* end;
WebPMux* mux = NULL;
WebPMuxImage* wpi = NULL;
const uint8_t* data;
size_t size;
WebPChunk chunk;
ChunkInit(&chunk);
// Sanity checks.
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) {
return NULL; // version mismatch
}
if (bitstream == NULL) return NULL;
data = bitstream->bytes_;
size = bitstream->size_;
if (data == NULL) return NULL;
if (size < RIFF_HEADER_SIZE) return NULL;
if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') ||
GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) {
return NULL;
}
mux = WebPMuxNew();
if (mux == NULL) return NULL;
if (size < RIFF_HEADER_SIZE + TAG_SIZE) goto Err;
tag = GetLE32(data + RIFF_HEADER_SIZE);
if (tag != kChunks[IDX_VP8].tag &&
tag != kChunks[IDX_VP8L].tag &&
tag != kChunks[IDX_VP8X].tag) {
goto Err; // First chunk should be VP8, VP8L or VP8X.
}
riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE));
if (riff_size > MAX_CHUNK_PAYLOAD || riff_size > size) {
goto Err;
} else {
if (riff_size < size) { // Redundant data after last chunk.
size = riff_size; // To make sure we don't read any data beyond mux_size.
}
}
end = data + size;
data += RIFF_HEADER_SIZE;
size -= RIFF_HEADER_SIZE;
wpi = (WebPMuxImage*)malloc(sizeof(*wpi));
if (wpi == NULL) goto Err;
MuxImageInit(wpi);
// Loop over chunks.
while (data != end) {
WebPChunkId id;
WebPMuxError err;
err = ChunkVerifyAndAssignData(&chunk, data, size, riff_size, copy_data);
if (err != WEBP_MUX_OK) goto Err;
id = ChunkGetIdFromTag(chunk.tag_);
if (IsWPI(id)) { // An image chunk (frame/tile/alpha/vp8).
WebPChunk** wpi_chunk_ptr =
MuxImageGetListFromId(wpi, id); // Image chunk to set.
assert(wpi_chunk_ptr != NULL);
if (*wpi_chunk_ptr != NULL) goto Err; // Consecutive alpha chunks or
// consecutive frame/tile chunks.
if (ChunkSetNth(&chunk, wpi_chunk_ptr, 1) != WEBP_MUX_OK) goto Err;
if (id == WEBP_CHUNK_IMAGE) {
wpi->is_partial_ = 0; // wpi is completely filled.
// Add this to mux->images_ list.
if (MuxImagePush(wpi, &mux->images_) != WEBP_MUX_OK) goto Err;
MuxImageInit(wpi); // Reset for reading next image.
} else {
wpi->is_partial_ = 1; // wpi is only partially filled.
}
} else { // A non-image chunk.
WebPChunk** chunk_list;
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
// getting all chunks of an image.
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
if (chunk_list == NULL) chunk_list = &mux->unknown_;
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
}
{
const size_t data_size = ChunkDiskSize(&chunk);
data += data_size;
size -= data_size;
}
ChunkInit(&chunk);
}
// Validate mux if complete.
if (MuxValidate(mux) != WEBP_MUX_OK) goto Err;
MuxImageDelete(wpi);
return mux; // All OK;
Err: // Something bad happened.
ChunkRelease(&chunk);
MuxImageDelete(wpi);
WebPMuxDelete(mux);
return NULL;
}
//------------------------------------------------------------------------------
// Get API(s).
WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, uint32_t* flags) {
WebPData data;
WebPMuxError err;
if (mux == NULL || flags == NULL) return WEBP_MUX_INVALID_ARGUMENT;
*flags = 0;
// Check if VP8X chunk is present.
err = MuxGet(mux, IDX_VP8X, 1, &data);
if (err == WEBP_MUX_NOT_FOUND) {
// Check if VP8/VP8L chunk is present.
err = WebPMuxGetImage(mux, &data);
WebPDataClear(&data);
return err;
} else if (err != WEBP_MUX_OK) {
return err;
}
if (data.size_ < CHUNK_SIZE_BYTES) return WEBP_MUX_BAD_DATA;
// All OK. Fill up flags.
*flags = GetLE32(data.bytes_);
return WEBP_MUX_OK;
}
static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width,
int height, uint32_t flags) {
const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
assert(width >= 1 && height >= 1);
assert(width <= MAX_CANVAS_SIZE && height <= MAX_CANVAS_SIZE);
assert(width * (uint64_t)height < MAX_IMAGE_AREA);
PutLE32(dst, MKFOURCC('V', 'P', '8', 'X'));
PutLE32(dst + TAG_SIZE, VP8X_CHUNK_SIZE);
PutLE32(dst + CHUNK_HEADER_SIZE, flags);
PutLE24(dst + CHUNK_HEADER_SIZE + 4, width - 1);
PutLE24(dst + CHUNK_HEADER_SIZE + 7, height - 1);
return dst + vp8x_size;
}
// Assemble a single image WebP bitstream from 'wpi'.
static WebPMuxError SynthesizeBitstream(WebPMuxImage* const wpi,
WebPData* const bitstream) {
uint8_t* dst;
// Allocate data.
const int need_vp8x = (wpi->alpha_ != NULL);
const size_t vp8x_size = need_vp8x ? CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE : 0;
const size_t alpha_size = need_vp8x ? ChunkDiskSize(wpi->alpha_) : 0;
// Note: No need to output FRM/TILE chunk for a single image.
const size_t size = RIFF_HEADER_SIZE + vp8x_size + alpha_size +
ChunkDiskSize(wpi->img_);
uint8_t* const data = (uint8_t*)malloc(size);
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
// Main RIFF header.
dst = MuxEmitRiffHeader(data, size);
if (need_vp8x) {
int w, h;
WebPMuxError err;
assert(wpi->img_ != NULL);
err = MuxGetImageWidthHeight(wpi->img_, &w, &h);
if (err != WEBP_MUX_OK) {
free(data);
return err;
}
dst = EmitVP8XChunk(dst, w, h, ALPHA_FLAG); // VP8X.
dst = ChunkListEmit(wpi->alpha_, dst); // ALPH.
}
// Bitstream.
dst = ChunkListEmit(wpi->img_, dst);
assert(dst == data + size);
// Output.
bitstream->bytes_ = data;
bitstream->size_ = size;
return WEBP_MUX_OK;
}
WebPMuxError WebPMuxGetImage(const WebPMux* mux, WebPData* bitstream) {
WebPMuxError err;
WebPMuxImage* wpi = NULL;
if (mux == NULL || bitstream == NULL) {
return WEBP_MUX_INVALID_ARGUMENT;
}
err = MuxValidateForImage(mux);
if (err != WEBP_MUX_OK) return err;
// All well. Get the image.
err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, WEBP_CHUNK_IMAGE,
&wpi);
assert(err == WEBP_MUX_OK); // Already tested above.
return SynthesizeBitstream(wpi, bitstream);
}
WebPMuxError WebPMuxGetMetadata(const WebPMux* mux, WebPData* metadata) {
if (mux == NULL || metadata == NULL) return WEBP_MUX_INVALID_ARGUMENT;
return MuxGet(mux, IDX_META, 1, metadata);
}
WebPMuxError WebPMuxGetColorProfile(const WebPMux* mux,
WebPData* color_profile) {
if (mux == NULL || color_profile == NULL) return WEBP_MUX_INVALID_ARGUMENT;
return MuxGet(mux, IDX_ICCP, 1, color_profile);
}
WebPMuxError WebPMuxGetLoopCount(const WebPMux* mux, int* loop_count) {
WebPData image;
WebPMuxError err;
if (mux == NULL || loop_count == NULL) return WEBP_MUX_INVALID_ARGUMENT;
err = MuxGet(mux, IDX_LOOP, 1, &image);
if (err != WEBP_MUX_OK) return err;
if (image.size_ < kChunks[WEBP_CHUNK_LOOP].size) return WEBP_MUX_BAD_DATA;
*loop_count = GetLE16(image.bytes_);
return WEBP_MUX_OK;
}
static WebPMuxError MuxGetFrameTileInternal(
const WebPMux* const mux, uint32_t nth, WebPData* const bitstream,
int* const x_offset, int* const y_offset, int* const duration,
uint32_t tag) {
const WebPData* frame_tile_data;
WebPMuxError err;
WebPMuxImage* wpi;
const int is_frame = (tag == kChunks[WEBP_CHUNK_FRAME].tag) ? 1 : 0;
const CHUNK_INDEX idx = is_frame ? IDX_FRAME : IDX_TILE;
const WebPChunkId id = kChunks[idx].id;
if (mux == NULL || bitstream == NULL ||
x_offset == NULL || y_offset == NULL || (is_frame && duration == NULL)) {
return WEBP_MUX_INVALID_ARGUMENT;
}
// Get the nth WebPMuxImage.
err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, id, &wpi);
if (err != WEBP_MUX_OK) return err;
// Get frame chunk.
assert(wpi->header_ != NULL); // As MuxImageGetNth() already checked header_.
frame_tile_data = &wpi->header_->data_;
if (frame_tile_data->size_ < kChunks[idx].size) return WEBP_MUX_BAD_DATA;
*x_offset = 2 * GetLE24(frame_tile_data->bytes_ + 0);
*y_offset = 2 * GetLE24(frame_tile_data->bytes_ + 3);
if (is_frame) *duration = 1 + GetLE24(frame_tile_data->bytes_ + 12);
return SynthesizeBitstream(wpi, bitstream);
}
WebPMuxError WebPMuxGetFrame(const WebPMux* mux, uint32_t nth,
WebPData* bitstream,
int* x_offset, int* y_offset, int* duration) {
return MuxGetFrameTileInternal(mux, nth, bitstream, x_offset, y_offset,
duration, kChunks[IDX_FRAME].tag);
}
WebPMuxError WebPMuxGetTile(const WebPMux* mux, uint32_t nth,
WebPData* bitstream,
int* x_offset, int* y_offset) {
return MuxGetFrameTileInternal(mux, nth, bitstream, x_offset, y_offset, NULL,
kChunks[IDX_TILE].tag);
}
// Get chunk index from chunk id. Returns IDX_NIL if not found.
static CHUNK_INDEX ChunkGetIndexFromId(WebPChunkId id) {
int i;
for (i = 0; kChunks[i].id != WEBP_CHUNK_NIL; ++i) {
if (id == kChunks[i].id) return i;
}
return IDX_NIL;
}
// Count number of chunks matching 'tag' in the 'chunk_list'.
// If tag == NIL_TAG, any tag will be matched.
static int CountChunks(const WebPChunk* const chunk_list, uint32_t tag) {
int count = 0;
const WebPChunk* current;
for (current = chunk_list; current != NULL; current = current->next_) {
if (tag == NIL_TAG || current->tag_ == tag) {
count++; // Count chunks whose tags match.
}
}
return count;
}
WebPMuxError WebPMuxNumChunks(const WebPMux* mux,
WebPChunkId id, int* num_elements) {
if (mux == NULL || num_elements == NULL) {
return WEBP_MUX_INVALID_ARGUMENT;
}
if (IsWPI(id)) {
*num_elements = MuxImageCount(mux->images_, id);
} else {
WebPChunk* const* chunk_list = MuxGetChunkListFromId(mux, id);
if (chunk_list == NULL) {
*num_elements = 0;
} else {
const CHUNK_INDEX idx = ChunkGetIndexFromId(id);
*num_elements = CountChunks(*chunk_list, kChunks[idx].tag);
}
}
return WEBP_MUX_OK;
}
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -1,13 +1,27 @@
AM_CPPFLAGS = -I$(top_srcdir)/src AM_CPPFLAGS = -I$(top_srcdir)/src
noinst_LTLIBRARIES = libwebputils.la
libwebputils_la_SOURCES =
libwebputils_la_SOURCES += bit_reader.c
libwebputils_la_SOURCES += bit_reader.h
libwebputils_la_SOURCES += bit_writer.c
libwebputils_la_SOURCES += bit_writer.h
libwebputils_la_SOURCES += color_cache.c
libwebputils_la_SOURCES += color_cache.h
libwebputils_la_SOURCES += filters.c
libwebputils_la_SOURCES += filters.h
libwebputils_la_SOURCES += huffman.c
libwebputils_la_SOURCES += huffman.h
libwebputils_la_SOURCES += huffman_encode.c
libwebputils_la_SOURCES += huffman_encode.h
libwebputils_la_SOURCES += quant_levels.c
libwebputils_la_SOURCES += quant_levels.h
libwebputils_la_SOURCES += rescaler.c
libwebputils_la_SOURCES += rescaler.h
libwebputils_la_SOURCES += thread.c
libwebputils_la_SOURCES += thread.h
libwebputils_la_SOURCES += utils.c
libwebputils_la_SOURCES += utils.h
libwebputils_la_SOURCES = bit_reader.h bit_reader.c \
bit_writer.h bit_writer.c \
thread.h thread.c
libwebputils_la_LDFLAGS = -version-info 0:0:0
libwebputils_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
libwebputilsinclude_HEADERS = ../webp/types.h libwebputilsinclude_HEADERS = ../webp/types.h
libwebputilsincludedir = $(includedir)/webp libwebputilsincludedir = $(includedir)/webp
noinst_HEADERS = bit_reader.h bit_writer.h thread.h
noinst_LTLIBRARIES = libwebputils.la

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -15,19 +15,21 @@
extern "C" { extern "C" {
#endif #endif
#define MK(X) (((bit_t)(X) << (BITS)) | (MASK))
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// VP8BitReader // VP8BitReader
void VP8InitBitReader(VP8BitReader* const br, void VP8InitBitReader(VP8BitReader* const br,
const uint8_t* const start, const uint8_t* const end) { const uint8_t* const start, const uint8_t* const end) {
assert(br); assert(br != NULL);
assert(start); assert(start != NULL);
assert(start <= end); assert(start <= end);
br->range_ = 255 - 1; br->range_ = MK(255 - 1);
br->buf_ = start; br->buf_ = start;
br->buf_end_ = end; br->buf_end_ = end;
br->value_ = 0; br->value_ = 0;
br->missing_ = 8; br->missing_ = 8; // to load the very first 8bits
br->eof_ = 0; br->eof_ = 0;
} }
@ -43,19 +45,39 @@ const uint8_t kVP8Log2Range[128] = {
0 0
}; };
// range = ((range + 1) << kVP8Log2Range[range]) - 1 // range = (range << kVP8Log2Range[range]) + trailing 1's
const uint8_t kVP8NewRange[128] = { const bit_t kVP8NewRange[128] = {
127, 127, 191, 127, 159, 191, 223, 127, 143, 159, 175, 191, 207, 223, 239, MK(127), MK(127), MK(191), MK(127), MK(159), MK(191), MK(223), MK(127),
127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239, MK(143), MK(159), MK(175), MK(191), MK(207), MK(223), MK(239), MK(127),
247, 127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, MK(135), MK(143), MK(151), MK(159), MK(167), MK(175), MK(183), MK(191),
183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, MK(199), MK(207), MK(215), MK(223), MK(231), MK(239), MK(247), MK(127),
243, 247, 251, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, MK(131), MK(135), MK(139), MK(143), MK(147), MK(151), MK(155), MK(159),
151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, MK(163), MK(167), MK(171), MK(175), MK(179), MK(183), MK(187), MK(191),
181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, MK(195), MK(199), MK(203), MK(207), MK(211), MK(215), MK(219), MK(223),
211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, MK(227), MK(231), MK(235), MK(239), MK(243), MK(247), MK(251), MK(127),
241, 243, 245, 247, 249, 251, 253, 127 MK(129), MK(131), MK(133), MK(135), MK(137), MK(139), MK(141), MK(143),
MK(145), MK(147), MK(149), MK(151), MK(153), MK(155), MK(157), MK(159),
MK(161), MK(163), MK(165), MK(167), MK(169), MK(171), MK(173), MK(175),
MK(177), MK(179), MK(181), MK(183), MK(185), MK(187), MK(189), MK(191),
MK(193), MK(195), MK(197), MK(199), MK(201), MK(203), MK(205), MK(207),
MK(209), MK(211), MK(213), MK(215), MK(217), MK(219), MK(221), MK(223),
MK(225), MK(227), MK(229), MK(231), MK(233), MK(235), MK(237), MK(239),
MK(241), MK(243), MK(245), MK(247), MK(249), MK(251), MK(253), MK(127)
}; };
#undef MK
void VP8LoadFinalBytes(VP8BitReader* const br) {
assert(br != NULL && br->buf_ != NULL);
// Only read 8bits at a time
if (br->buf_ < br->buf_end_) {
br->value_ |= (bit_t)(*br->buf_++) << ((BITS) - 8 + br->missing_);
br->missing_ -= 8;
} else {
br->eof_ = 1;
}
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Higher-level calls // Higher-level calls
@ -72,6 +94,134 @@ int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) {
return VP8Get(br) ? -value : value; return VP8Get(br) ? -value : value;
} }
//------------------------------------------------------------------------------
// VP8LBitReader
#define MAX_NUM_BIT_READ 25
static const uint32_t kBitMask[MAX_NUM_BIT_READ] = {
0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767,
65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607, 16777215
};
void VP8LInitBitReader(VP8LBitReader* const br,
const uint8_t* const start,
size_t length) {
size_t i;
assert(br != NULL);
assert(start != NULL);
assert(length < 0xfffffff8u); // can't happen with a RIFF chunk.
br->buf_ = start;
br->len_ = length;
br->val_ = 0;
br->pos_ = 0;
br->bit_pos_ = 0;
br->eos_ = 0;
br->error_ = 0;
for (i = 0; i < sizeof(br->val_) && i < br->len_; ++i) {
br->val_ |= ((uint64_t)br->buf_[br->pos_]) << (8 * i);
++br->pos_;
}
}
void VP8LBitReaderSetBuffer(VP8LBitReader* const br,
const uint8_t* const buf, size_t len) {
assert(br != NULL);
assert(buf != NULL);
assert(len < 0xfffffff8u); // can't happen with a RIFF chunk.
br->eos_ = (br->pos_ >= len);
br->buf_ = buf;
br->len_ = len;
}
static void ShiftBytes(VP8LBitReader* const br) {
while (br->bit_pos_ >= 8 && br->pos_ < br->len_) {
br->val_ >>= 8;
br->val_ |= ((uint64_t)br->buf_[br->pos_]) << 56;
++br->pos_;
br->bit_pos_ -= 8;
}
}
void VP8LFillBitWindow(VP8LBitReader* const br) {
if (br->bit_pos_ >= 32) {
#if defined(__x86_64__) || defined(_M_X64)
if (br->pos_ + 8 < br->len_) {
br->val_ >>= 32;
// The expression below needs a little-endian arch to work correctly.
// This gives a large speedup for decoding speed.
br->val_ |= *(const uint64_t *)(br->buf_ + br->pos_) << 32;
br->pos_ += 4;
br->bit_pos_ -= 32;
} else {
// Slow path.
ShiftBytes(br);
}
#else
// Always the slow path.
ShiftBytes(br);
#endif
}
if (br->pos_ == br->len_ && br->bit_pos_ == 64) {
br->eos_ = 1;
}
}
uint32_t VP8LReadOneBit(VP8LBitReader* const br) {
const uint32_t val = (br->val_ >> br->bit_pos_) & 1;
// Flag an error at end_of_stream.
if (!br->eos_) {
++br->bit_pos_;
if (br->bit_pos_ >= 32) {
ShiftBytes(br);
}
// After this last bit is read, check if eos needs to be flagged.
if (br->pos_ == br->len_ && br->bit_pos_ == 64) {
br->eos_ = 1;
}
} else {
br->error_ = 1;
}
return val;
}
uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits) {
uint32_t val = 0;
assert(n_bits >= 0);
// Flag an error if end_of_stream or n_bits is more than allowed limit.
if (!br->eos_ && n_bits < MAX_NUM_BIT_READ) {
// If this read is going to cross the read buffer, set the eos flag.
if (br->pos_ == br->len_) {
if ((br->bit_pos_ + n_bits) >= 64) {
br->eos_ = 1;
if ((br->bit_pos_ + n_bits) > 64) return val;
}
}
val = (br->val_ >> br->bit_pos_) & kBitMask[n_bits];
br->bit_pos_ += n_bits;
if (br->bit_pos_ >= 40) {
if (br->pos_ + 5 < br->len_) {
br->val_ >>= 40;
br->val_ |=
(((uint64_t)br->buf_[br->pos_ + 0]) << 24) |
(((uint64_t)br->buf_[br->pos_ + 1]) << 32) |
(((uint64_t)br->buf_[br->pos_ + 2]) << 40) |
(((uint64_t)br->buf_[br->pos_ + 3]) << 48) |
(((uint64_t)br->buf_[br->pos_ + 4]) << 56);
br->pos_ += 5;
br->bit_pos_ -= 40;
}
if (br->bit_pos_ >= 8) {
ShiftBytes(br);
}
}
} else {
br->error_ = 1;
}
return val;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)

View File

@ -1,4 +1,4 @@
// Copyright 2010 Google Inc. // Copyright 2010 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -8,17 +8,35 @@
// Boolean decoder // Boolean decoder
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
// Vikas Arora (vikaas.arora@gmail.com)
#ifndef WEBP_UTILS_BIT_READER_H_ #ifndef WEBP_UTILS_BIT_READER_H_
#define WEBP_UTILS_BIT_READER_H_ #define WEBP_UTILS_BIT_READER_H_
#include <assert.h> #include <assert.h>
#include "../webp/decode_vp8.h" #ifdef _MSC_VER
#include <stdlib.h> // _byteswap_ulong
#endif
#include <string.h> // For memcpy
#include "../webp/types.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
#define BITS 32 // can be 32, 16 or 8
#define MASK ((((bit_t)1) << (BITS)) - 1)
#if (BITS == 32)
typedef uint64_t bit_t; // natural register type
typedef uint32_t lbit_t; // natural type for memory I/O
#elif (BITS == 16)
typedef uint32_t bit_t;
typedef uint16_t lbit_t;
#else
typedef uint32_t bit_t;
typedef uint8_t lbit_t;
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Bitreader and code-tree reader // Bitreader and code-tree reader
@ -29,9 +47,9 @@ struct VP8BitReader {
int eof_; // true if input is exhausted int eof_; // true if input is exhausted
// boolean decoder // boolean decoder
uint32_t range_; // current range minus 1. In [127, 254] interval. bit_t range_; // current range minus 1. In [127, 254] interval.
uint32_t value_; // current value bit_t value_; // current value
int missing_; // number of missing bits in value_ (8bit) int missing_; // number of missing bits in value_ (8bit)
}; };
// Initialize the bit reader and the boolean decoder. // Initialize the bit reader and the boolean decoder.
@ -40,7 +58,7 @@ void VP8InitBitReader(VP8BitReader* const br,
// return the next value made of 'num_bits' bits // return the next value made of 'num_bits' bits
uint32_t VP8GetValue(VP8BitReader* const br, int num_bits); uint32_t VP8GetValue(VP8BitReader* const br, int num_bits);
static inline uint32_t VP8Get(VP8BitReader* const br) { static WEBP_INLINE uint32_t VP8Get(VP8BitReader* const br) {
return VP8GetValue(br, 1); return VP8GetValue(br, 1);
} }
@ -49,59 +67,129 @@ int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits);
// Read a bit with proba 'prob'. Speed-critical function! // Read a bit with proba 'prob'. Speed-critical function!
extern const uint8_t kVP8Log2Range[128]; extern const uint8_t kVP8Log2Range[128];
extern const uint8_t kVP8NewRange[128]; extern const bit_t kVP8NewRange[128];
static inline uint32_t VP8GetByte(VP8BitReader* const br) {
assert(br);
if (br->buf_ < br->buf_end_) {
assert(br->buf_);
return *br->buf_++;
}
br->eof_ = 1;
return 0xff;
}
static inline uint32_t VP8BitUpdate(VP8BitReader* const br, uint32_t split) { void VP8LoadFinalBytes(VP8BitReader* const br); // special case for the tail
uint32_t bit;
const uint32_t value_split = (split + 1) << 8; static WEBP_INLINE void VP8LoadNewBytes(VP8BitReader* const br) {
// Make sure we have a least 8 bits in 'value_' assert(br && br->buf_);
if (br->missing_ > 0) { // Read 'BITS' bits at a time if possible.
br->value_ |= VP8GetByte(br) << br->missing_; if (br->buf_ + sizeof(lbit_t) <= br->buf_end_) {
br->missing_ -= 8; // convert memory type to register type (with some zero'ing!)
} bit_t bits;
bit = (br->value_ >= value_split); lbit_t in_bits = *(lbit_t*)br->buf_;
if (bit) { br->buf_ += (BITS) >> 3;
br->range_ -= split + 1; #if !defined(__BIG_ENDIAN__)
br->value_ -= value_split; #if (BITS == 32)
#if defined(__i386__) || defined(__x86_64__)
__asm__ volatile("bswap %k0" : "=r"(in_bits) : "0"(in_bits));
bits = (bit_t)in_bits; // 32b -> 64b zero-extension
#elif defined(_MSC_VER)
bits = _byteswap_ulong(in_bits);
#else
bits = (bit_t)(in_bits >> 24) | ((in_bits >> 8) & 0xff00)
| ((in_bits << 8) & 0xff0000) | (in_bits << 24);
#endif // x86
#elif (BITS == 16)
// gcc will recognize a 'rorw $8, ...' here:
bits = (bit_t)(in_bits >> 8) | ((in_bits & 0xff) << 8);
#endif
#else // LITTLE_ENDIAN
bits = (bit_t)in_bits;
#endif
br->value_ |= bits << br->missing_;
br->missing_ -= (BITS);
} else { } else {
br->range_ = split; VP8LoadFinalBytes(br); // no need to be inlined
} }
return bit;
} }
static inline void VP8Shift(VP8BitReader* const br) { static WEBP_INLINE int VP8BitUpdate(VP8BitReader* const br, bit_t split) {
const bit_t value_split = split | (MASK);
if (br->missing_ > 0) { // Make sure we have a least BITS bits in 'value_'
VP8LoadNewBytes(br);
}
if (br->value_ > value_split) {
br->range_ -= value_split + 1;
br->value_ -= value_split + 1;
return 1;
} else {
br->range_ = value_split;
return 0;
}
}
static WEBP_INLINE void VP8Shift(VP8BitReader* const br) {
// range_ is in [0..127] interval here. // range_ is in [0..127] interval here.
const int shift = kVP8Log2Range[br->range_]; const int idx = br->range_ >> (BITS);
br->range_ = kVP8NewRange[br->range_]; const int shift = kVP8Log2Range[idx];
br->range_ = kVP8NewRange[idx];
br->value_ <<= shift; br->value_ <<= shift;
br->missing_ += shift; br->missing_ += shift;
} }
static inline uint32_t VP8GetBit(VP8BitReader* const br, int prob) { static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) {
const uint32_t split = (br->range_ * prob) >> 8; // It's important to avoid generating a 64bit x 64bit multiply here.
const uint32_t bit = VP8BitUpdate(br, split); // We just need an 8b x 8b after all.
if (br->range_ < 0x7f) { const bit_t split =
(bit_t)((uint32_t)(br->range_ >> (BITS)) * prob) << ((BITS) - 8);
const int bit = VP8BitUpdate(br, split);
if (br->range_ <= (((bit_t)0x7e << (BITS)) | (MASK))) {
VP8Shift(br); VP8Shift(br);
} }
return bit; return bit;
} }
static inline int VP8GetSigned(VP8BitReader* const br, int v) { static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v) {
const uint32_t split = br->range_ >> 1; const bit_t split = (br->range_ >> 1);
const uint32_t bit = VP8BitUpdate(br, split); const int bit = VP8BitUpdate(br, split);
VP8Shift(br); VP8Shift(br);
return bit ? -v : v; return bit ? -v : v;
} }
// -----------------------------------------------------------------------------
// Bitreader
typedef struct {
uint64_t val_;
const uint8_t* buf_;
size_t len_;
size_t pos_;
int bit_pos_;
int eos_;
int error_;
} VP8LBitReader;
void VP8LInitBitReader(VP8LBitReader* const br,
const uint8_t* const start,
size_t length);
// Sets a new data buffer.
void VP8LBitReaderSetBuffer(VP8LBitReader* const br,
const uint8_t* const buffer, size_t length);
// Reads the specified number of bits from Read Buffer.
// Flags an error in case end_of_stream or n_bits is more than allowed limit.
// Flags eos if this read attempt is going to cross the read buffer.
uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits);
// Reads one bit from Read Buffer. Flags an error in case end_of_stream.
// Flags eos after reading last bit from the buffer.
uint32_t VP8LReadOneBit(VP8LBitReader* const br);
// VP8LReadOneBitUnsafe is faster than VP8LReadOneBit, but it can be called only
// 32 times after the last VP8LFillBitWindow. Any subsequent calls
// (without VP8LFillBitWindow) will return invalid data.
static WEBP_INLINE uint32_t VP8LReadOneBitUnsafe(VP8LBitReader* const br) {
const uint32_t val = (br->val_ >> br->bit_pos_) & 1;
++br->bit_pos_;
return val;
}
// Advances the Read buffer by 4 bytes to make room for reading next 32 bits.
void VP8LFillBitWindow(VP8LBitReader* const br);
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -8,6 +8,7 @@
// Bit writing and boolean coder // Bit writing and boolean coder
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
// Vikas Arora (vikaas.arora@gmail.com)
#include <assert.h> #include <assert.h>
#include <string.h> // for memcpy() #include <string.h> // for memcpy()
@ -24,18 +25,23 @@ extern "C" {
static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) { static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) {
uint8_t* new_buf; uint8_t* new_buf;
size_t new_size; size_t new_size;
const size_t needed_size = bw->pos_ + extra_size; const uint64_t needed_size_64b = (uint64_t)bw->pos_ + extra_size;
const size_t needed_size = (size_t)needed_size_64b;
if (needed_size_64b != needed_size) {
bw->error_ = 1;
return 0;
}
if (needed_size <= bw->max_pos_) return 1; if (needed_size <= bw->max_pos_) return 1;
// If the following line wraps over 32bit, the test just after will catch it.
new_size = 2 * bw->max_pos_; new_size = 2 * bw->max_pos_;
if (new_size < needed_size) if (new_size < needed_size) new_size = needed_size;
new_size = needed_size;
if (new_size < 1024) new_size = 1024; if (new_size < 1024) new_size = 1024;
new_buf = (uint8_t*)malloc(new_size); new_buf = (uint8_t*)malloc(new_size);
if (new_buf == NULL) { if (new_buf == NULL) {
bw->error_ = 1; bw->error_ = 1;
return 0; return 0;
} }
if (bw->pos_ > 0) memcpy(new_buf, bw->buf_, bw->pos_); memcpy(new_buf, bw->buf_, bw->pos_);
free(bw->buf_); free(bw->buf_);
bw->buf_ = new_buf; bw->buf_ = new_buf;
bw->max_pos_ = new_size; bw->max_pos_ = new_size;
@ -50,10 +56,8 @@ static void kFlush(VP8BitWriter* const bw) {
bw->nb_bits_ -= 8; bw->nb_bits_ -= 8;
if ((bits & 0xff) != 0xff) { if ((bits & 0xff) != 0xff) {
size_t pos = bw->pos_; size_t pos = bw->pos_;
if (pos + bw->run_ >= bw->max_pos_) { // reallocate if (!BitWriterResize(bw, bw->run_ + 1)) {
if (!BitWriterResize(bw, bw->run_ + 1)) { return;
return;
}
} }
if (bits & 0x100) { // overflow -> propagate carry over pending 0xff's if (bits & 0x100) { // overflow -> propagate carry over pending 0xff's
if (pos > 0) bw->buf_[pos - 1]++; if (pos > 0) bw->buf_[pos - 1]++;
@ -179,6 +183,100 @@ int VP8BitWriterAppend(VP8BitWriter* const bw,
return 1; return 1;
} }
void VP8BitWriterWipeOut(VP8BitWriter* const bw) {
if (bw) {
free(bw->buf_);
memset(bw, 0, sizeof(*bw));
}
}
//------------------------------------------------------------------------------
// VP8LBitWriter
// Returns 1 on success.
static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) {
uint8_t* allocated_buf;
size_t allocated_size;
const size_t current_size = VP8LBitWriterNumBytes(bw);
const uint64_t size_required_64b = (uint64_t)current_size + extra_size;
const size_t size_required = (size_t)size_required_64b;
if (size_required != size_required_64b) {
bw->error_ = 1;
return 0;
}
if (bw->max_bytes_ > 0 && size_required <= bw->max_bytes_) return 1;
allocated_size = (3 * bw->max_bytes_) >> 1;
if (allocated_size < size_required) allocated_size = size_required;
// make allocated size multiple of 1k
allocated_size = (((allocated_size >> 10) + 1) << 10);
allocated_buf = (uint8_t*)malloc(allocated_size);
if (allocated_buf == NULL) {
bw->error_ = 1;
return 0;
}
memcpy(allocated_buf, bw->buf_, current_size);
free(bw->buf_);
bw->buf_ = allocated_buf;
bw->max_bytes_ = allocated_size;
memset(allocated_buf + current_size, 0, allocated_size - current_size);
return 1;
}
int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size) {
memset(bw, 0, sizeof(*bw));
return VP8LBitWriterResize(bw, expected_size);
}
void VP8LBitWriterDestroy(VP8LBitWriter* const bw) {
if (bw != NULL) {
free(bw->buf_);
memset(bw, 0, sizeof(*bw));
}
}
void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits) {
if (n_bits < 1) return;
#if !defined(__BIG_ENDIAN__)
// Technically, this branch of the code can write up to 25 bits at a time,
// but in prefix encoding, the maximum number of bits written is 18 at a time.
{
uint8_t* const p = &bw->buf_[bw->bit_pos_ >> 3];
uint32_t v = *(const uint32_t*)p;
v |= bits << (bw->bit_pos_ & 7);
*(uint32_t*)p = v;
bw->bit_pos_ += n_bits;
}
#else // BIG_ENDIAN
{
uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3];
const int bits_reserved_in_first_byte = bw->bit_pos_ & 7;
const int bits_left_to_write = n_bits - 8 + bits_reserved_in_first_byte;
// implicit & 0xff is assumed for uint8_t arithmetics
*p++ |= bits << bits_reserved_in_first_byte;
bits >>= 8 - bits_reserved_in_first_byte;
if (bits_left_to_write >= 1) {
*p++ = bits;
bits >>= 8;
if (bits_left_to_write >= 9) {
*p++ = bits;
bits >>= 8;
}
}
assert(n_bits <= 25);
*p = bits;
bw->bit_pos_ += n_bits;
}
#endif
if ((bw->bit_pos_ >> 3) > (bw->max_bytes_ - 8)) {
const uint64_t extra_size = 32768ULL + bw->max_bytes_;
if (extra_size != (size_t)extra_size ||
!VP8LBitWriterResize(bw, (size_t)extra_size)) {
bw->bit_pos_ = 0;
bw->error_ = 1;
}
}
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google Inc. // Copyright 2011 Google Inc. All Rights Reserved.
// //
// This code is licensed under the same terms as WebM: // This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/ // Software License Agreement: http://www.webmproject.org/license/software/
@ -27,33 +27,93 @@ struct VP8BitWriter {
int32_t value_; int32_t value_;
int run_; // number of outstanding bits int run_; // number of outstanding bits
int nb_bits_; // number of pending bits int nb_bits_; // number of pending bits
uint8_t* buf_; uint8_t* buf_; // internal buffer. Re-allocated regularly. Not owned.
size_t pos_; size_t pos_;
size_t max_pos_; size_t max_pos_;
int error_; // true in case of error int error_; // true in case of error
}; };
// Initialize the object. Allocates some initial memory based on expected_size.
int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size); int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size);
// Finalize the bitstream coding. Returns a pointer to the internal buffer.
uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw); uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw);
// Release any pending memory and zeroes the object. Not a mandatory call.
// Only useful in case of error, when the internal buffer hasn't been grabbed!
void VP8BitWriterWipeOut(VP8BitWriter* const bw);
int VP8PutBit(VP8BitWriter* const bw, int bit, int prob); int VP8PutBit(VP8BitWriter* const bw, int bit, int prob);
int VP8PutBitUniform(VP8BitWriter* const bw, int bit); int VP8PutBitUniform(VP8BitWriter* const bw, int bit);
void VP8PutValue(VP8BitWriter* const bw, int value, int nb_bits); void VP8PutValue(VP8BitWriter* const bw, int value, int nb_bits);
void VP8PutSignedValue(VP8BitWriter* const bw, int value, int nb_bits); void VP8PutSignedValue(VP8BitWriter* const bw, int value, int nb_bits);
// Appends some bytes to the internal buffer. Data is copied.
int VP8BitWriterAppend(VP8BitWriter* const bw, int VP8BitWriterAppend(VP8BitWriter* const bw,
const uint8_t* data, size_t size); const uint8_t* data, size_t size);
// return approximate write position (in bits) // return approximate write position (in bits)
static inline uint64_t VP8BitWriterPos(const VP8BitWriter* const bw) { static WEBP_INLINE uint64_t VP8BitWriterPos(const VP8BitWriter* const bw) {
return (uint64_t)(bw->pos_ + bw->run_) * 8 + 8 + bw->nb_bits_; return (uint64_t)(bw->pos_ + bw->run_) * 8 + 8 + bw->nb_bits_;
} }
static inline uint8_t* VP8BitWriterBuf(const VP8BitWriter* const bw) { // Returns a pointer to the internal buffer.
static WEBP_INLINE uint8_t* VP8BitWriterBuf(const VP8BitWriter* const bw) {
return bw->buf_; return bw->buf_;
} }
static inline size_t VP8BitWriterSize(const VP8BitWriter* const bw) { // Returns the size of the internal buffer.
static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) {
return bw->pos_; return bw->pos_;
} }
//------------------------------------------------------------------------------
// VP8LBitWriter
// TODO(vikasa): VP8LBitWriter is copied as-is from lossless code. There's scope
// of re-using VP8BitWriter. Will evaluate once basic lossless encoder is
// implemented.
typedef struct {
uint8_t* buf_;
size_t bit_pos_;
size_t max_bytes_;
// After all bits are written, the caller must observe the state of
// error_. A value of 1 indicates that a memory allocation failure
// has happened during bit writing. A value of 0 indicates successful
// writing of bits.
int error_;
} VP8LBitWriter;
static WEBP_INLINE size_t VP8LBitWriterNumBytes(VP8LBitWriter* const bw) {
return (bw->bit_pos_ + 7) >> 3;
}
static WEBP_INLINE uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) {
return bw->buf_;
}
// Returns 0 in case of memory allocation error.
int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size);
void VP8LBitWriterDestroy(VP8LBitWriter* const bw);
// This function writes bits into bytes in increasing addresses, and within
// a byte least-significant-bit first.
//
// The function can write up to 16 bits in one go with WriteBits
// Example: let's assume that 3 bits (Rs below) have been written already:
//
// BYTE-0 BYTE+1 BYTE+2
//
// 0000 0RRR 0000 0000 0000 0000
//
// Now, we could write 5 or less bits in MSB by just sifting by 3
// and OR'ing to BYTE-0.
//
// For n bits, we take the last 5 bytes, OR that with high bits in BYTE-0,
// and locate the rest in BYTE+1 and BYTE+2.
//
// VP8LBitWriter's error_ flag is set in case of memory allocation error.
void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)

44
src/utils/color_cache.c Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Color Cache for WebP Lossless
//
// Author: Jyrki Alakuijala (jyrki@google.com)
#include <assert.h>
#include <stdlib.h>
#include "./color_cache.h"
#include "../utils/utils.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------
// VP8LColorCache.
int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) {
const int hash_size = 1 << hash_bits;
assert(cc != NULL);
assert(hash_bits > 0);
cc->colors_ = (uint32_t*)WebPSafeCalloc((uint64_t)hash_size,
sizeof(*cc->colors_));
if (cc->colors_ == NULL) return 0;
cc->hash_shift_ = 32 - hash_bits;
return 1;
}
void VP8LColorCacheClear(VP8LColorCache* const cc) {
if (cc != NULL) {
free(cc->colors_);
cc->colors_ = NULL;
}
}
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif

68
src/utils/color_cache.h Normal file
View File

@ -0,0 +1,68 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Color Cache for WebP Lossless
//
// Authors: Jyrki Alakuijala (jyrki@google.com)
// Urvang Joshi (urvang@google.com)
#ifndef WEBP_UTILS_COLOR_CACHE_H_
#define WEBP_UTILS_COLOR_CACHE_H_
#include "../webp/types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
// Main color cache struct.
typedef struct {
uint32_t *colors_; // color entries
int hash_shift_; // Hash shift: 32 - hash_bits.
} VP8LColorCache;
static const uint32_t kHashMul = 0x1e35a7bd;
static WEBP_INLINE uint32_t VP8LColorCacheLookup(
const VP8LColorCache* const cc, uint32_t key) {
assert(key <= (~0U >> cc->hash_shift_));
return cc->colors_[key];
}
static WEBP_INLINE void VP8LColorCacheInsert(const VP8LColorCache* const cc,
uint32_t argb) {
const uint32_t key = (kHashMul * argb) >> cc->hash_shift_;
cc->colors_[key] = argb;
}
static WEBP_INLINE int VP8LColorCacheGetIndex(const VP8LColorCache* const cc,
uint32_t argb) {
return (kHashMul * argb) >> cc->hash_shift_;
}
static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc,
uint32_t argb) {
const uint32_t key = (kHashMul * argb) >> cc->hash_shift_;
return cc->colors_[key] == argb;
}
//------------------------------------------------------------------------------
// Initializes the color cache with 'hash_bits' bits for the keys.
// Returns false in case of memory error.
int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits);
// Delete the memory associated to color cache.
void VP8LColorCacheClear(VP8LColorCache* const color_cache);
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif // WEBP_UTILS_COLOR_CACHE_H_

229
src/utils/filters.c Normal file
View File

@ -0,0 +1,229 @@
// Copyright 2011 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Spatial prediction using various filters
//
// Author: Urvang (urvang@google.com)
#include "./filters.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------
// Helpful macro.
# define SANITY_CHECK(in, out) \
assert(in != NULL); \
assert(out != NULL); \
assert(width > 0); \
assert(height > 0); \
assert(bpp > 0); \
assert(stride >= width * bpp);
static WEBP_INLINE void PredictLine(const uint8_t* src, const uint8_t* pred,
uint8_t* dst, int length, int inverse) {
int i;
if (inverse) {
for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i];
} else {
for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i];
}
}
//------------------------------------------------------------------------------
// Horizontal filter.
static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in,
int width, int height, int bpp, int stride, int inverse, uint8_t* out) {
int h;
const uint8_t* preds = (inverse ? out : in);
SANITY_CHECK(in, out);
// Filter line-by-line.
for (h = 0; h < height; ++h) {
// Leftmost pixel is predicted from above (except for topmost scanline).
if (h == 0) {
memcpy((void*)out, (const void*)in, bpp);
} else {
PredictLine(in, preds - stride, out, bpp, inverse);
}
PredictLine(in + bpp, preds, out + bpp, bpp * (width - 1), inverse);
preds += stride;
in += stride;
out += stride;
}
}
static void HorizontalFilter(const uint8_t* data, int width, int height,
int bpp, int stride, uint8_t* filtered_data) {
DoHorizontalFilter(data, width, height, bpp, stride, 0, filtered_data);
}
static void HorizontalUnfilter(const uint8_t* data, int width, int height,
int bpp, int stride, uint8_t* recon_data) {
DoHorizontalFilter(data, width, height, bpp, stride, 1, recon_data);
}
//------------------------------------------------------------------------------
// Vertical filter.
static WEBP_INLINE void DoVerticalFilter(const uint8_t* in,
int width, int height, int bpp, int stride, int inverse, uint8_t* out) {
int h;
const uint8_t* preds = (inverse ? out : in);
SANITY_CHECK(in, out);
// Very first top-left pixel is copied.
memcpy((void*)out, (const void*)in, bpp);
// Rest of top scan-line is left-predicted.
PredictLine(in + bpp, preds, out + bpp, bpp * (width - 1), inverse);
// Filter line-by-line.
for (h = 1; h < height; ++h) {
in += stride;
out += stride;
PredictLine(in, preds, out, bpp * width, inverse);
preds += stride;
}
}
static void VerticalFilter(const uint8_t* data, int width, int height,
int bpp, int stride, uint8_t* filtered_data) {
DoVerticalFilter(data, width, height, bpp, stride, 0, filtered_data);
}
static void VerticalUnfilter(const uint8_t* data, int width, int height,
int bpp, int stride, uint8_t* recon_data) {
DoVerticalFilter(data, width, height, bpp, stride, 1, recon_data);
}
//------------------------------------------------------------------------------
// Gradient filter.
static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) {
const int g = a + b - c;
return (g < 0) ? 0 : (g > 255) ? 255 : g;
}
static WEBP_INLINE
void DoGradientFilter(const uint8_t* in, int width, int height,
int bpp, int stride, int inverse, uint8_t* out) {
const uint8_t* preds = (inverse ? out : in);
int h;
SANITY_CHECK(in, out);
// left prediction for top scan-line
memcpy((void*)out, (const void*)in, bpp);
PredictLine(in + bpp, preds, out + bpp, bpp * (width - 1), inverse);
// Filter line-by-line.
for (h = 1; h < height; ++h) {
int w;
preds += stride;
in += stride;
out += stride;
// leftmost pixel: predict from above.
PredictLine(in, preds - stride, out, bpp, inverse);
for (w = bpp; w < width * bpp; ++w) {
const int pred = GradientPredictor(preds[w - bpp],
preds[w - stride],
preds[w - stride - bpp]);
out[w] = in[w] + (inverse ? pred : -pred);
}
}
}
static void GradientFilter(const uint8_t* data, int width, int height,
int bpp, int stride, uint8_t* filtered_data) {
DoGradientFilter(data, width, height, bpp, stride, 0, filtered_data);
}
static void GradientUnfilter(const uint8_t* data, int width, int height,
int bpp, int stride, uint8_t* recon_data) {
DoGradientFilter(data, width, height, bpp, stride, 1, recon_data);
}
#undef SANITY_CHECK
// -----------------------------------------------------------------------------
// Quick estimate of a potentially interesting filter mode to try, in addition
// to the default NONE.
#define SMAX 16
#define SDIFF(a, b) (abs((a) - (b)) >> 4) // Scoring diff, in [0..SMAX)
WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
int width, int height, int stride) {
int i, j;
int bins[WEBP_FILTER_LAST][SMAX];
memset(bins, 0, sizeof(bins));
// We only sample every other pixels. That's enough.
for (j = 2; j < height - 1; j += 2) {
const uint8_t* const p = data + j * stride;
int mean = p[0];
for (i = 2; i < width - 1; i += 2) {
const int diff0 = SDIFF(p[i], mean);
const int diff1 = SDIFF(p[i], p[i - 1]);
const int diff2 = SDIFF(p[i], p[i - width]);
const int grad_pred =
GradientPredictor(p[i - 1], p[i - width], p[i - width - 1]);
const int diff3 = SDIFF(p[i], grad_pred);
bins[WEBP_FILTER_NONE][diff0] = 1;
bins[WEBP_FILTER_HORIZONTAL][diff1] = 1;
bins[WEBP_FILTER_VERTICAL][diff2] = 1;
bins[WEBP_FILTER_GRADIENT][diff3] = 1;
mean = (3 * mean + p[i] + 2) >> 2;
}
}
{
WEBP_FILTER_TYPE filter, best_filter = WEBP_FILTER_NONE;
int best_score = 0x7fffffff;
for (filter = WEBP_FILTER_NONE; filter < WEBP_FILTER_LAST; ++filter) {
int score = 0;
for (i = 0; i < SMAX; ++i) {
if (bins[filter][i] > 0) {
score += i;
}
}
if (score < best_score) {
best_score = score;
best_filter = filter;
}
}
return best_filter;
}
}
#undef SMAX
#undef SDIFF
//------------------------------------------------------------------------------
const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = {
NULL, // WEBP_FILTER_NONE
HorizontalFilter, // WEBP_FILTER_HORIZONTAL
VerticalFilter, // WEBP_FILTER_VERTICAL
GradientFilter // WEBP_FILTER_GRADIENT
};
const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST] = {
NULL, // WEBP_FILTER_NONE
HorizontalUnfilter, // WEBP_FILTER_HORIZONTAL
VerticalUnfilter, // WEBP_FILTER_VERTICAL
GradientUnfilter // WEBP_FILTER_GRADIENT
};
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

54
src/utils/filters.h Normal file
View File

@ -0,0 +1,54 @@
// Copyright 2011 Google Inc. All Rights Reserved.
//
// This code is licensed under the same terms as WebM:
// Software License Agreement: http://www.webmproject.org/license/software/
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Spatial prediction using various filters
//
// Author: Urvang (urvang@google.com)
#ifndef WEBP_UTILS_FILTERS_H_
#define WEBP_UTILS_FILTERS_H_
#include "../webp/types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
// Filters.
typedef enum {
WEBP_FILTER_NONE = 0,
WEBP_FILTER_HORIZONTAL,
WEBP_FILTER_VERTICAL,
WEBP_FILTER_GRADIENT,
WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1, // end marker
WEBP_FILTER_BEST,
WEBP_FILTER_FAST
} WEBP_FILTER_TYPE;
typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
int bpp, int stride, uint8_t* out);
// Filter the given data using the given predictor.
// 'in' corresponds to a 2-dimensional pixel array of size (stride * height)
// in raster order.
// 'bpp' is number of bytes per pixel, and
// 'stride' is number of bytes per scan line (with possible padding).
// 'out' should be pre-allocated.
extern const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
// Reconstruct the original data from the given filtered data.
extern const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST];
// Fast estimate of a potentially good filter.
extern WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
int width, int height, int stride);
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
#endif /* WEBP_UTILS_FILTERS_H_ */

Some files were not shown because too many files have changed in this diff Show More