1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
27 #include "JpegWriter.hxx"
28 #include <vcl/BitmapReadAccess.hxx>
29 #include <vcl/FilterConfigItem.hxx>
30 #include <tools/helpers.hxx>
31 #include <tools/stream.hxx>
33 #define BUFFER_SIZE 4096
37 struct DestinationManagerStruct
39 jpeg_destination_mgr pub
; /* public fields */
40 SvStream
* stream
; /* target stream */
41 JOCTET
* buffer
; /* start of buffer */
48 static void init_destination (j_compress_ptr cinfo
)
50 DestinationManagerStruct
* destination
= reinterpret_cast<DestinationManagerStruct
*>(cinfo
->dest
);
52 /* Allocate the output buffer -- it will be released when done with image */
53 destination
->buffer
= static_cast<JOCTET
*>(
54 (*cinfo
->mem
->alloc_small
) (reinterpret_cast<j_common_ptr
>(cinfo
), JPOOL_IMAGE
, BUFFER_SIZE
* sizeof(JOCTET
)));
56 destination
->pub
.next_output_byte
= destination
->buffer
;
57 destination
->pub
.free_in_buffer
= BUFFER_SIZE
;
60 static boolean
empty_output_buffer (j_compress_ptr cinfo
)
62 DestinationManagerStruct
* destination
= reinterpret_cast<DestinationManagerStruct
*>(cinfo
->dest
);
64 if (destination
->stream
->WriteBytes(destination
->buffer
, BUFFER_SIZE
) != BUFFER_SIZE
)
66 ERREXIT(cinfo
, JERR_FILE_WRITE
);
69 destination
->pub
.next_output_byte
= destination
->buffer
;
70 destination
->pub
.free_in_buffer
= BUFFER_SIZE
;
75 static void term_destination (j_compress_ptr cinfo
)
77 DestinationManagerStruct
* destination
= reinterpret_cast<DestinationManagerStruct
*>(cinfo
->dest
);
78 size_t datacount
= BUFFER_SIZE
- destination
->pub
.free_in_buffer
;
80 /* Write any data remaining in the buffer */
83 if (destination
->stream
->WriteBytes(destination
->buffer
, datacount
) != datacount
)
85 ERREXIT(cinfo
, JERR_FILE_WRITE
);
92 void jpeg_svstream_dest (j_compress_ptr cinfo
, void* output
)
94 SvStream
* stream
= static_cast<SvStream
*>(output
);
95 DestinationManagerStruct
* destination
;
97 /* The destination object is made permanent so that multiple JPEG images
98 * can be written to the same file without re-executing jpeg_svstream_dest.
99 * This makes it dangerous to use this manager and a different destination
100 * manager serially with the same JPEG object, because their private object
101 * sizes may be different. Caveat programmer.
103 if (cinfo
->dest
== nullptr)
104 { /* first time for this JPEG object? */
105 cinfo
->dest
= static_cast<jpeg_destination_mgr
*>(
106 (*cinfo
->mem
->alloc_small
) (reinterpret_cast<j_common_ptr
>(cinfo
), JPOOL_PERMANENT
, sizeof(DestinationManagerStruct
)));
109 destination
= reinterpret_cast<DestinationManagerStruct
*>(cinfo
->dest
);
110 destination
->pub
.init_destination
= init_destination
;
111 destination
->pub
.empty_output_buffer
= empty_output_buffer
;
112 destination
->pub
.term_destination
= term_destination
;
113 destination
->stream
= stream
;
116 JPEGWriter::JPEGWriter( SvStream
& rStream
, const css::uno::Sequence
< css::beans::PropertyValue
>* pFilterData
, bool* pExportWasGrey
) :
117 mrStream ( rStream
),
118 mpBuffer ( nullptr ),
120 mpExpWasGrey ( pExportWasGrey
)
122 FilterConfigItem
aConfigItem( pFilterData
);
123 mbGreys
= aConfigItem
.ReadInt32( u
"ColorMode"_ustr
, 0 ) != 0;
124 mnQuality
= aConfigItem
.ReadInt32( u
"Quality"_ustr
, 75 );
125 maChromaSubsampling
= aConfigItem
.ReadInt32( u
"ChromaSubsamplingMode"_ustr
, 0 );
129 for( const auto& rValue
: *pFilterData
)
131 if ( rValue
.Name
== "StatusIndicator" )
133 rValue
.Value
>>= mxStatusIndicator
;
139 void* JPEGWriter::GetScanline( tools::Long nY
)
141 void* pScanline
= nullptr;
147 pScanline
= mpReadAccess
->GetScanline( nY
);
152 tools::Long nWidth
= mpReadAccess
->Width();
153 sal_uInt8
* pTmp
= mpBuffer
;
155 if( mpReadAccess
->HasPalette() )
157 Scanline pScanlineRead
= mpReadAccess
->GetScanline( nY
);
158 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
160 aColor
= mpReadAccess
->GetPaletteColor( mpReadAccess
->GetIndexFromData( pScanlineRead
, nX
) );
161 *pTmp
++ = aColor
.GetRed();
164 *pTmp
++ = aColor
.GetGreen();
165 *pTmp
++ = aColor
.GetBlue();
171 Scanline pScanlineRead
= mpReadAccess
->GetScanline( nY
);
172 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
174 aColor
= mpReadAccess
->GetPixelFromData( pScanlineRead
, nX
);
175 *pTmp
++ = aColor
.GetRed();
178 *pTmp
++ = aColor
.GetGreen();
179 *pTmp
++ = aColor
.GetBlue();
184 pScanline
= mpBuffer
;
191 bool JPEGWriter::Write( const Graphic
& rGraphic
)
195 if ( mxStatusIndicator
.is() )
197 mxStatusIndicator
->start( OUString(), 100 );
200 // This slightly weird logic is here to match the behaviour in ImpGraphic::ImplGetBitmap
201 // and is necessary to match pre-existing behaviour. We should probably pass down the expected
202 // background color for alpha from the higher layers.
204 if (rGraphic
.GetType() == GraphicType::Bitmap
)
205 aGraphicBmp
= rGraphic
.GetBitmapEx().GetBitmap(COL_WHITE
);
207 aGraphicBmp
= rGraphic
.GetBitmapEx().GetBitmap();
211 if ( !aGraphicBmp
.Convert( BmpConversion::N8BitGreys
) )
212 aGraphicBmp
= rGraphic
.GetBitmapEx().GetBitmap();
215 mpReadAccess
= aGraphicBmp
;
218 if ( !mbGreys
) // bitmap was not explicitly converted into greyscale,
219 { // check if source is greyscale only
222 tools::Long nWidth
= mpReadAccess
->Width();
223 for ( tools::Long nY
= 0; bIsGrey
&& ( nY
< mpReadAccess
->Height() ); nY
++ )
226 Scanline pScanlineRead
= mpReadAccess
->GetScanline( nY
);
227 for( tools::Long nX
= 0; bIsGrey
&& ( nX
< nWidth
); nX
++ )
229 aColor
= mpReadAccess
->HasPalette() ? mpReadAccess
->GetPaletteColor( mpReadAccess
->GetIndexFromData( pScanlineRead
, nX
) )
230 : mpReadAccess
->GetPixelFromData( pScanlineRead
, nX
);
231 bIsGrey
= ( aColor
.GetRed() == aColor
.GetGreen() ) && ( aColor
.GetRed() == aColor
.GetBlue() );
238 *mpExpWasGrey
= mbGreys
;
241 mbNative
= ( mpReadAccess
->GetScanlineFormat() == ScanlineFormat::N8BitPal
&& aGraphicBmp
.HasGreyPalette8Bit());
243 mbNative
= ( mpReadAccess
->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb
);
246 mpBuffer
= new sal_uInt8
[ AlignedWidth4Bytes( mbGreys
? mpReadAccess
->Width() * 8L : mpReadAccess
->Width() * 24L ) ];
248 SAL_INFO("vcl", "\nJPEG Export - DPI X: " << rGraphic
.GetPPI().getWidth() << "\nJPEG Export - DPI Y: " << rGraphic
.GetPPI().getHeight());
250 bRet
= WriteJPEG( this, &mrStream
, mpReadAccess
->Width(),
251 mpReadAccess
->Height(), rGraphic
.GetPPI(), mbGreys
,
252 mnQuality
, maChromaSubsampling
, mxStatusIndicator
);
257 mpReadAccess
.reset();
259 if ( mxStatusIndicator
.is() )
260 mxStatusIndicator
->end();
265 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */