mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-23 04:35:46 +00:00
Use jpeg-turbo encoder
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -51,6 +51,7 @@ dependencies:
|
||||
- uuid == 1.3.*
|
||||
- zip == 2.0.*
|
||||
- JuicyPixels
|
||||
- JuicyPixels-jpeg-turbo
|
||||
- JuicyPixels-stbir
|
||||
|
||||
flags:
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user