Use jpeg-turbo encoder

This commit is contained in:
IC Rainbow
2024-11-12 18:25:21 +02:00
parent d0e5f18329
commit b7b34d4ab5
7 changed files with 33 additions and 15 deletions
+6
View File
@@ -63,3 +63,9 @@ source-repository-package
location: https://github.com/simplex-chat/wai.git
tag: 2f6e5aa5f05ba9140ac99e195ee647b4f7d926b0
subdir: warp
source-repository-package
type: git
location: https://gitlab.com/dpwiz/hs-jpeg-turbo
tag: bd65878bd29933c7cee262c3b7bf82186a32812c
subdir: JuicyPixels-jpeg-turbo jpeg-turbo
+1
View File
@@ -51,6 +51,7 @@ dependencies:
- uuid == 1.3.*
- zip == 2.0.*
- JuicyPixels
- JuicyPixels-jpeg-turbo
- JuicyPixels-stbir
flags:
+7
View File
@@ -200,6 +200,7 @@ library
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns
build-depends:
JuicyPixels
, JuicyPixels-jpeg-turbo
, JuicyPixels-stbir
, aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
@@ -265,6 +266,7 @@ executable simplex-bot
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
build-depends:
JuicyPixels
, JuicyPixels-jpeg-turbo
, JuicyPixels-stbir
, aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
@@ -331,6 +333,7 @@ executable simplex-bot-advanced
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
build-depends:
JuicyPixels
, JuicyPixels-jpeg-turbo
, JuicyPixels-stbir
, aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
@@ -400,6 +403,7 @@ executable simplex-broadcast-bot
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
build-depends:
JuicyPixels
, JuicyPixels-jpeg-turbo
, JuicyPixels-stbir
, aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
@@ -467,6 +471,7 @@ executable simplex-chat
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
build-depends:
JuicyPixels
, JuicyPixels-jpeg-turbo
, JuicyPixels-stbir
, aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
@@ -540,6 +545,7 @@ executable simplex-directory-service
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
build-depends:
JuicyPixels
, JuicyPixels-jpeg-turbo
, JuicyPixels-stbir
, aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
@@ -640,6 +646,7 @@ test-suite simplex-chat-test
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
build-depends:
JuicyPixels
, JuicyPixels-jpeg-turbo
, JuicyPixels-stbir
, QuickCheck ==2.14.*
, aeson ==2.2.*
+12 -12
View File
@@ -10,7 +10,7 @@
module Simplex.Chat.Image where
import qualified Codec.Picture as Picture
import Codec.Picture.Jpg (encodeDirectJpegAtQualityWithMetadata)
import qualified Codec.Picture.JPEGTurbo as JT
import Codec.Picture.Metadata (Metadatas)
import Codec.Picture.Png (encodePng)
import qualified Codec.Picture.STBIR as STBIR
@@ -21,12 +21,12 @@ import qualified Data.ByteString.Base64.Lazy as LB64
import qualified Data.ByteString.Lazy.Char8 as LB
import Data.Int (Int64)
import Data.Monoid (All (..))
import Data.Word (Word8)
import System.IO.Unsafe (unsafePerformIO)
resizeImageToSize :: Bool -> Int64 -> ResizeableImage -> LB.ByteString
resizeImageToSize toURI maxSize (ResizeableImage fmt img encoder) = either resizePNG resizeJPG encoder
resizeImageToSize :: Bool -> Int -> Int64 -> ResizeableImage -> LB.ByteString
resizeImageToSize toURI minJpegQuality maxSize (ResizeableImage fmt img encoder) = either resizePNG resizeJPG encoder
where
halveAndRetry = resizeImageToSize toURI maxSize $ ResizeableImage fmt imgHalved encoder
halveAndRetry = resizeImageToSize toURI minJpegQuality maxSize (ResizeableImage fmt imgHalved encoder)
imgHalved = downscale img 2.0
resizePNG enc
| LB.length encoded <= maxSize = encoded
@@ -49,12 +49,12 @@ resizeImageToSize toURI maxSize (ResizeableImage fmt img encoder) = either resiz
result = encode $ downscale img m
resizeJPG enc
| minSize > maxSize = halveAndRetry
| otherwise = fitQuality 50 99
| otherwise = fitQuality minJpegQuality 95
where
encode q
| toURI = toDataUri "jpg" $ enc q img -- the correct mime type is "jpeg", but only "jpg" is supported by older clients
| otherwise = enc q img
minSize = LB.length $ encode 50
minSize = LB.length $ encode minJpegQuality
fitQuality l u
| u - l <= 1 = encode l -- prefer higher compression
| otherwise =
@@ -107,21 +107,21 @@ data ResizeableImage where
type ImageEncoder a = Either (PNGEncoder a) (JPGEncoder a)
type PNGEncoder a = Picture.Image a -> LB.ByteString
type JPGEncoder a = Word8 -> Picture.Image a -> LB.ByteString
type JPGEncoder a = Int -> Picture.Image a -> LB.ByteString
resizeableImage :: Picture.DynamicImage -> Maybe ResizeableImage
resizeableImage dyn = case dyn of
Picture.ImageY8 img -> Just $ ResizeableImage "Y8" img $ Right $ \q -> encodeDirectJpegAtQualityWithMetadata q mempty
Picture.ImageY8 img -> Just $ ResizeableImage "Y8" img $ Right $ \q -> LB.fromStrict . unsafePerformIO . JT.encodeRGB q . Picture.convertRGB8 . Picture.ImageY8 -- TODO: directly
Picture.ImageY16 img -> Just $ ResizeableImage "Y16" img $ Left encodePng
Picture.ImageY32 _ -> Nothing
Picture.ImageYF _ -> Nothing
Picture.ImageYA8 img -> Just $ ResizeableImage "YA8" img $ Left encodePng
Picture.ImageYA16 img -> Just $ ResizeableImage "YA16" img $ Left encodePng
Picture.ImageRGB8 img -> Just $ ResizeableImage "RGB8" img $ Right $ \q -> encodeDirectJpegAtQualityWithMetadata q mempty
Picture.ImageRGB8 img -> Just $ ResizeableImage "RGB8" img $ Right $ \q -> LB.fromStrict . unsafePerformIO . JT.encodeRGB q
Picture.ImageRGB16 img -> Just $ ResizeableImage "RGB16" img $ Left encodePng
Picture.ImageRGBF _ -> Nothing
Picture.ImageRGBA8 img -> Just $ ResizeableImage "RGBA8" img $ Left encodePng
Picture.ImageRGBA16 img -> Just $ ResizeableImage "RGBA16" img $ Left encodePng
Picture.ImageYCbCr8 img -> Just $ ResizeableImage "YCbCr8" img $ Right $ \q -> encodeDirectJpegAtQualityWithMetadata q mempty
Picture.ImageCMYK8 img -> Just $ ResizeableImage "CMYK8" img $ Right $ \q -> encodeDirectJpegAtQualityWithMetadata q mempty
Picture.ImageYCbCr8 img -> Just $ ResizeableImage "YCbCr8" img $ Right $ \q -> LB.fromStrict . unsafePerformIO . JT.encodeRGB q . Picture.convertRGB8 . Picture.ImageYCbCr8 -- TODO: JT.encodeYUV
Picture.ImageCMYK8 img -> Just $ ResizeableImage "CMYK8" img $ Right $ \q -> LB.fromStrict . unsafePerformIO . JT.encodeRGB q . Picture.convertRGB8 . Picture.ImageCMYK8
Picture.ImageCMYK16 _ -> Nothing
+3 -1
View File
@@ -210,9 +210,11 @@ cChatResizeImageToStrSize fp' maxSize = do
fp <- peekCString fp'
res <- runExceptT $ do
(ri, _) <- liftIOEither $ readResizeable fp
let resized = resizeImageToSize True (fromIntegral maxSize) ri
let resized = resizeImageToSize True previewMinQuality (fromIntegral maxSize) ri
if LB.length resized > fromIntegral maxSize then throwError "unable to fit" else pure resized
newCStringFromLazyBS $ fromRight "" res
where
previewMinQuality = 20
-- -- | Strip EXIF etc metadata from image, inlplace
-- cChatStripImageMetadata :: CString -> IO CBool
+3 -1
View File
@@ -82,11 +82,13 @@ cChatWriteImage cc maxSize cPath ptr len encrypt = do
case Picture.decodeResizeable src of
Left e -> pure $ WFError e
Right (ri, _metadata) -> do
let resized = resizeImageToSize False (fromIntegral maxSize) ri
let resized = resizeImageToSize False storeMinQuality (fromIntegral maxSize) ri
if LB.length resized > fromIntegral maxSize
then pure $ WFError "unable to fit"
else chatWriteFile_ cfArgs_ path (LB.toStrict resized)
newCStringFromLazyBS $ J.encode r
where
storeMinQuality = 20
data ReadFileResult
= RFResult {fileSize :: Int}
+1 -1
View File
@@ -21,7 +21,7 @@ resizeToStrTest :: FilePath -> FilePath -> IO ()
resizeToStrTest inputPath tmp = do
(ri@(ResizeableImage imgFormat _img encoder), metadata) <- either error pure =<< Image.readResizeable inputPath
logDebug $ tshow (metadata, imgFormat, either (const "png") (const "jpg") encoder)
let res = Image.resizeImageToSize True maxSize ri
let res = Image.resizeImageToSize True 20 maxSize ri
finalSize = LB.length res
unless (finalSize <= maxSize) $ error $ "Final size larger than maximum size: " <> show (finalSize, maxSize)
let (fmt, b64) = fmap (LB.drop 8) . LB.break (== ';') $ LB.drop 11 res