LibreOffice Module vcl (master) 1
JpegWriter.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
23#include "jpeg.h"
24#include <jpeglib.h>
25#include <jerror.h>
26
27#include "JpegWriter.hxx"
30#include <tools/helpers.hxx>
31#include <tools/stream.hxx>
32
33#define BUFFER_SIZE 4096
34
35namespace {
36
37struct DestinationManagerStruct
38{
39 jpeg_destination_mgr pub; /* public fields */
40 SvStream* stream; /* target stream */
41 JOCTET * buffer; /* start of buffer */
42};
43
44}
45
46extern "C" {
47
48static void init_destination (j_compress_ptr cinfo)
49{
50 DestinationManagerStruct * destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
51
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)));
55
56 destination->pub.next_output_byte = destination->buffer;
57 destination->pub.free_in_buffer = BUFFER_SIZE;
58}
59
60static boolean empty_output_buffer (j_compress_ptr cinfo)
61{
62 DestinationManagerStruct * destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
63
64 if (destination->stream->WriteBytes(destination->buffer, BUFFER_SIZE) != BUFFER_SIZE)
65 {
66 ERREXIT(cinfo, JERR_FILE_WRITE);
67 }
68
69 destination->pub.next_output_byte = destination->buffer;
70 destination->pub.free_in_buffer = BUFFER_SIZE;
71
72 return TRUE;
73}
74
75static void term_destination (j_compress_ptr cinfo)
76{
77 DestinationManagerStruct * destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
78 size_t datacount = BUFFER_SIZE - destination->pub.free_in_buffer;
79
80 /* Write any data remaining in the buffer */
81 if (datacount > 0)
82 {
83 if (destination->stream->WriteBytes(destination->buffer, datacount) != datacount)
84 {
85 ERREXIT(cinfo, JERR_FILE_WRITE);
86 }
87 }
88}
89
90}
91
92void jpeg_svstream_dest (j_compress_ptr cinfo, void* output)
93{
94 SvStream* stream = static_cast<SvStream*>(output);
95 DestinationManagerStruct * destination;
96
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.
102 */
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)));
107 }
108
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;
114}
115
116JPEGWriter::JPEGWriter( SvStream& rStream, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData, bool* pExportWasGrey ) :
117 mrStream ( rStream ),
118 mpBuffer ( nullptr ),
119 mbNative ( false ),
120 mpExpWasGrey ( pExportWasGrey )
121{
122 FilterConfigItem aConfigItem( pFilterData );
123 mbGreys = aConfigItem.ReadInt32( "ColorMode", 0 ) != 0;
124 mnQuality = aConfigItem.ReadInt32( "Quality", 75 );
125 maChromaSubsampling = aConfigItem.ReadInt32( "ChromaSubsamplingMode", 0 );
126
127 if ( pFilterData )
128 {
129 for( const auto& rValue : *pFilterData )
130 {
131 if ( rValue.Name == "StatusIndicator" )
132 {
133 rValue.Value >>= mxStatusIndicator;
134 }
135 }
136 }
137}
138
140{
141 void* pScanline = nullptr;
142
143 if( mpReadAccess )
144 {
145 if( mbNative )
146 {
147 pScanline = mpReadAccess->GetScanline( nY );
148 }
149 else if( mpBuffer )
150 {
151 BitmapColor aColor;
152 tools::Long nWidth = mpReadAccess->Width();
153 sal_uInt8* pTmp = mpBuffer;
154
155 if( mpReadAccess->HasPalette() )
156 {
157 Scanline pScanlineRead = mpReadAccess->GetScanline( nY );
158 for( tools::Long nX = 0; nX < nWidth; nX++ )
159 {
160 aColor = mpReadAccess->GetPaletteColor( mpReadAccess->GetIndexFromData( pScanlineRead, nX ) );
161 *pTmp++ = aColor.GetRed();
162 if ( !mbGreys )
163 {
164 *pTmp++ = aColor.GetGreen();
165 *pTmp++ = aColor.GetBlue();
166 }
167 }
168 }
169 else
170 {
171 Scanline pScanlineRead = mpReadAccess->GetScanline( nY );
172 for( tools::Long nX = 0; nX < nWidth; nX++ )
173 {
174 aColor = mpReadAccess->GetPixelFromData( pScanlineRead, nX );
175 *pTmp++ = aColor.GetRed();
176 if ( !mbGreys )
177 {
178 *pTmp++ = aColor.GetGreen();
179 *pTmp++ = aColor.GetBlue();
180 }
181 }
182 }
183
184 pScanline = mpBuffer;
185 }
186 }
187
188 return pScanline;
189}
190
191bool JPEGWriter::Write( const Graphic& rGraphic )
192{
193 bool bRet = false;
194
195 if ( mxStatusIndicator.is() )
196 {
197 mxStatusIndicator->start( OUString(), 100 );
198 }
199
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.
203 Bitmap aGraphicBmp;
204 if (rGraphic.GetType() == GraphicType::Bitmap)
205 aGraphicBmp = rGraphic.GetBitmapEx().GetBitmap(COL_WHITE);
206 else
207 aGraphicBmp = rGraphic.GetBitmapEx().GetBitmap();
208
209 if ( mbGreys )
210 {
211 if ( !aGraphicBmp.Convert( BmpConversion::N8BitGreys ) )
212 aGraphicBmp = rGraphic.GetBitmapEx().GetBitmap();
213 }
214
216 if( mpReadAccess )
217 {
218 if ( !mbGreys ) // bitmap was not explicitly converted into greyscale,
219 { // check if source is greyscale only
220 bool bIsGrey = true;
221
222 tools::Long nWidth = mpReadAccess->Width();
223 for ( tools::Long nY = 0; bIsGrey && ( nY < mpReadAccess->Height() ); nY++ )
224 {
225 BitmapColor aColor;
226 Scanline pScanlineRead = mpReadAccess->GetScanline( nY );
227 for( tools::Long nX = 0; bIsGrey && ( nX < nWidth ); nX++ )
228 {
230 : mpReadAccess->GetPixelFromData( pScanlineRead, nX );
231 bIsGrey = ( aColor.GetRed() == aColor.GetGreen() ) && ( aColor.GetRed() == aColor.GetBlue() );
232 }
233 }
234 if ( bIsGrey )
235 mbGreys = true;
236 }
237 if( mpExpWasGrey )
239
240 if ( mbGreys )
242 else
244
245 if( !mbNative )
247
248 SAL_INFO("vcl", "\nJPEG Export - DPI X: " << rGraphic.GetPPI().getWidth() << "\nJPEG Export - DPI Y: " << rGraphic.GetPPI().getHeight());
249
250 bRet = WriteJPEG( this, &mrStream, mpReadAccess->Width(),
251 mpReadAccess->Height(), rGraphic.GetPPI(), mbGreys,
253
254 delete[] mpBuffer;
255 mpBuffer = nullptr;
256
258 }
259 if ( mxStatusIndicator.is() )
260 mxStatusIndicator->end();
261
262 return bRet;
263}
264
265/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static void init_destination(j_compress_ptr cinfo)
Definition: JpegWriter.cxx:48
void jpeg_svstream_dest(j_compress_ptr cinfo, void *output)
Definition: JpegWriter.cxx:92
static boolean empty_output_buffer(j_compress_ptr cinfo)
Definition: JpegWriter.cxx:60
#define BUFFER_SIZE
Definition: JpegWriter.cxx:33
static void term_destination(j_compress_ptr cinfo)
Definition: JpegWriter.cxx:75
sal_uInt8 * Scanline
Definition: Scanline.hxx:26
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:217
tools::Long Height() const
tools::Long Width() const
bool HasPalette() const
ScanlineFormat GetScanlineFormat() const
const BitmapColor & GetPaletteColor(sal_uInt16 nColor) const
BitmapColor GetPixelFromData(const sal_uInt8 *pData, tools::Long nX) const
sal_uInt8 GetIndexFromData(const sal_uInt8 *pData, tools::Long nX) const
Scanline GetScanline(tools::Long nY) const
bool HasGreyPalette8Bit() const
bool Convert(BmpConversion eConversion)
Convert bitmap format.
vcl::ScopedBitmapAccess< BitmapReadAccess, Bitmap, &Bitmap::AcquireReadAccess > ScopedReadAccess
sal_uInt8 GetBlue() const
sal_uInt8 GetRed() const
sal_uInt8 GetGreen() const
sal_Int32 ReadInt32(const OUString &rKey, sal_Int32 nDefault)
basegfx::B2DSize GetPPI() const
Definition: graph.cxx:386
GraphicType GetType() const
Definition: graph.cxx:294
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
Definition: graph.cxx:330
sal_Int32 mnQuality
Definition: JpegWriter.hxx:37
SvStream & mrStream
Definition: JpegWriter.hxx:32
bool mbNative
Definition: JpegWriter.hxx:35
bool mbGreys
Definition: JpegWriter.hxx:36
sal_uInt8 * mpBuffer
Definition: JpegWriter.hxx:34
Bitmap::ScopedReadAccess mpReadAccess
Definition: JpegWriter.hxx:33
void * GetScanline(tools::Long nY)
Definition: JpegWriter.cxx:139
css::uno::Reference< css::task::XStatusIndicator > mxStatusIndicator
Definition: JpegWriter.hxx:42
bool Write(const Graphic &rGraphic)
Definition: JpegWriter.cxx:191
bool * mpExpWasGrey
Definition: JpegWriter.hxx:40
sal_Int32 maChromaSubsampling
Definition: JpegWriter.hxx:38
JPEGWriter(SvStream &rStream, const css::uno::Sequence< css::beans::PropertyValue > *pFilterData, bool *pExportWasGrey)
Definition: JpegWriter.cxx:116
TYPE getWidth() const
TYPE getHeight() const
constexpr ::Color COL_WHITE(0xFF, 0xFF, 0xFF)
Reference< XOutputStream > stream
sal_uInt32 AlignedWidth4Bytes(sal_uInt32 nWidthBits)
#define TRUE
bool WriteJPEG(JPEGWriter *pJPEGWriter, void *pOutputStream, tools::Long nWidth, tools::Long nHeight, basegfx::B2DSize const &aPPI, bool bGreyScale, tools::Long nQualityPercent, tools::Long aChromaSubsampling, css::uno::Reference< css::task::XStatusIndicator > const &status)
Definition: jpegc.cxx:367
#define SAL_INFO(area, stream)
long Long
unsigned char sal_uInt8