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>
26 #include "JpegWriter.hxx"
27 #include <vcl/bitmapaccess.hxx>
28 #include <vcl/FilterConfigItem.hxx>
29 #include <vcl/graphicfilter.hxx>
31 #define BUFFER_SIZE 4096
33 struct DestinationManagerStruct
35 jpeg_destination_mgr pub
; /* public fields */
36 SvStream
* stream
; /* target stream */
37 JOCTET
* buffer
; /* start of buffer */
40 extern "C" void init_destination (j_compress_ptr cinfo
)
42 DestinationManagerStruct
* destination
= reinterpret_cast<DestinationManagerStruct
*>(cinfo
->dest
);
44 /* Allocate the output buffer -- it will be released when done with image */
45 destination
->buffer
= static_cast<JOCTET
*>(
46 (*cinfo
->mem
->alloc_small
) (reinterpret_cast<j_common_ptr
>(cinfo
), JPOOL_IMAGE
, BUFFER_SIZE
* sizeof(JOCTET
)));
48 destination
->pub
.next_output_byte
= destination
->buffer
;
49 destination
->pub
.free_in_buffer
= BUFFER_SIZE
;
52 extern "C" boolean
empty_output_buffer (j_compress_ptr cinfo
)
54 DestinationManagerStruct
* destination
= reinterpret_cast<DestinationManagerStruct
*>(cinfo
->dest
);
56 if (destination
->stream
->WriteBytes(destination
->buffer
, BUFFER_SIZE
) != BUFFER_SIZE
)
58 ERREXIT(cinfo
, JERR_FILE_WRITE
);
61 destination
->pub
.next_output_byte
= destination
->buffer
;
62 destination
->pub
.free_in_buffer
= BUFFER_SIZE
;
67 extern "C" void term_destination (j_compress_ptr cinfo
)
69 DestinationManagerStruct
* destination
= reinterpret_cast<DestinationManagerStruct
*>(cinfo
->dest
);
70 size_t datacount
= BUFFER_SIZE
- destination
->pub
.free_in_buffer
;
72 /* Write any data remaining in the buffer */
75 if (destination
->stream
->WriteBytes(destination
->buffer
, datacount
) != datacount
)
77 ERREXIT(cinfo
, JERR_FILE_WRITE
);
82 void jpeg_svstream_dest (j_compress_ptr cinfo
, void* output
)
84 SvStream
* stream
= static_cast<SvStream
*>(output
);
85 DestinationManagerStruct
* destination
;
87 /* The destination object is made permanent so that multiple JPEG images
88 * can be written to the same file without re-executing jpeg_svstream_dest.
89 * This makes it dangerous to use this manager and a different destination
90 * manager serially with the same JPEG object, because their private object
91 * sizes may be different. Caveat programmer.
93 if (cinfo
->dest
== nullptr)
94 { /* first time for this JPEG object? */
95 cinfo
->dest
= static_cast<jpeg_destination_mgr
*>(
96 (*cinfo
->mem
->alloc_small
) (reinterpret_cast<j_common_ptr
>(cinfo
), JPOOL_PERMANENT
, sizeof(DestinationManagerStruct
)));
99 destination
= reinterpret_cast<DestinationManagerStruct
*>(cinfo
->dest
);
100 destination
->pub
.init_destination
= init_destination
;
101 destination
->pub
.empty_output_buffer
= empty_output_buffer
;
102 destination
->pub
.term_destination
= term_destination
;
103 destination
->stream
= stream
;
106 JPEGWriter::JPEGWriter( SvStream
& rStream
, const css::uno::Sequence
< css::beans::PropertyValue
>* pFilterData
, bool* pExportWasGrey
) :
107 mrStream ( rStream
),
108 mpBuffer ( nullptr ),
110 mpExpWasGrey ( pExportWasGrey
)
112 FilterConfigItem
aConfigItem( const_cast<css::uno::Sequence
< css::beans::PropertyValue
>*>(pFilterData
) );
113 mbGreys
= aConfigItem
.ReadInt32( "ColorMode", 0 ) != 0;
114 mnQuality
= aConfigItem
.ReadInt32( "Quality", 75 );
115 maChromaSubsampling
= aConfigItem
.ReadInt32( "ChromaSubsamplingMode", 0 );
119 int nArgs
= pFilterData
->getLength();
120 const css::beans::PropertyValue
* pValues
= pFilterData
->getConstArray();
123 if ( pValues
->Name
== "StatusIndicator" )
125 pValues
->Value
>>= mxStatusIndicator
;
132 void* JPEGWriter::GetScanline( long nY
)
134 void* pScanline
= nullptr;
140 pScanline
= mpReadAccess
->GetScanline( nY
);
145 long nWidth
= mpReadAccess
->Width();
146 sal_uInt8
* pTmp
= mpBuffer
;
148 if( mpReadAccess
->HasPalette() )
150 for( long nX
= 0; nX
< nWidth
; nX
++ )
152 aColor
= mpReadAccess
->GetPaletteColor( mpReadAccess
->GetPixelIndex( nY
, nX
) );
153 *pTmp
++ = aColor
.GetRed();
156 *pTmp
++ = aColor
.GetGreen();
157 *pTmp
++ = aColor
.GetBlue();
163 for( long nX
= 0; nX
< nWidth
; nX
++ )
165 aColor
= mpReadAccess
->GetPixel( nY
, nX
);
166 *pTmp
++ = aColor
.GetRed();
169 *pTmp
++ = aColor
.GetGreen();
170 *pTmp
++ = aColor
.GetBlue();
175 pScanline
= mpBuffer
;
182 bool JPEGWriter::Write( const Graphic
& rGraphic
)
186 if ( mxStatusIndicator
.is() )
189 mxStatusIndicator
->start( aMsg
, 100 );
192 Bitmap
aGraphicBmp( rGraphic
.GetBitmap() );
196 if ( !aGraphicBmp
.Convert( BmpConversion::N8BitGreys
) )
197 aGraphicBmp
= rGraphic
.GetBitmap();
200 mpReadAccess
= Bitmap::ScopedReadAccess(aGraphicBmp
);
203 if ( !mbGreys
) // bitmap was not explicitly converted into greyscale,
204 { // check if source is greyscale only
207 long nWidth
= mpReadAccess
->Width();
208 for ( long nY
= 0; bIsGrey
&& ( nY
< mpReadAccess
->Height() ); nY
++ )
211 for( long nX
= 0; bIsGrey
&& ( nX
< nWidth
); nX
++ )
213 aColor
= mpReadAccess
->HasPalette() ? mpReadAccess
->GetPaletteColor( mpReadAccess
->GetPixelIndex( nY
, nX
) )
214 : mpReadAccess
->GetPixel( nY
, nX
);
215 bIsGrey
= ( aColor
.GetRed() == aColor
.GetGreen() ) && ( aColor
.GetRed() == aColor
.GetBlue() );
222 *mpExpWasGrey
= mbGreys
;
224 mbNative
= ( mpReadAccess
->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb
);
227 mpBuffer
= new sal_uInt8
[ AlignedWidth4Bytes( mbGreys
? mpReadAccess
->Width() * 8L : mpReadAccess
->Width() * 24L ) ];
229 SAL_INFO("vcl", "\nJPEG Export - DPI X: " << rGraphic
.GetPPI().getX() << "\nJPEG Export - DPI Y: " << rGraphic
.GetPPI().getY());
231 bRet
= WriteJPEG( this, &mrStream
, mpReadAccess
->Width(),
232 mpReadAccess
->Height(), rGraphic
.GetPPI(), mbGreys
,
233 mnQuality
, maChromaSubsampling
, mxStatusIndicator
);
238 mpReadAccess
.reset();
240 if ( mxStatusIndicator
.is() )
241 mxStatusIndicator
->end();
246 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */