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"
28 #include <vcl/bitmapaccess.hxx>
29 #include <vcl/FilterConfigItem.hxx>
30 #include <tools/helpers.hxx>
31 #include <tools/stream.hxx>
32 
33 #define BUFFER_SIZE 4096
34 
35 namespace {
36 
37 struct DestinationManagerStruct
38 {
39  jpeg_destination_mgr pub; /* public fields */
40  SvStream* stream; /* target stream */
41  JOCTET * buffer; /* start of buffer */
42 };
43 
44 }
45 
46 extern "C" {
47 
48 static 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 
60 static 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 
75 static 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 
92 void 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 
116 JPEGWriter::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 
139 void* JPEGWriter::GetScanline( long nY )
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  long nWidth = mpReadAccess->Width();
153  sal_uInt8* pTmp = mpBuffer;
154 
155  if( mpReadAccess->HasPalette() )
156  {
157  Scanline pScanlineRead = mpReadAccess->GetScanline( nY );
158  for( 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( 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 
191 bool JPEGWriter::Write( const Graphic& rGraphic )
192 {
193  bool bRet = false;
194 
195  if ( mxStatusIndicator.is() )
196  {
197  mxStatusIndicator->start( OUString(), 100 );
198  }
199 
200  Bitmap aGraphicBmp( rGraphic.GetBitmapEx().GetBitmap(COL_WHITE) );
201 
202  if ( mbGreys )
203  {
204  if ( !aGraphicBmp.Convert( BmpConversion::N8BitGreys ) )
205  aGraphicBmp = rGraphic.GetBitmapEx().GetBitmap();
206  }
207 
208  mpReadAccess = Bitmap::ScopedReadAccess(aGraphicBmp);
209  if( mpReadAccess )
210  {
211  if ( !mbGreys ) // bitmap was not explicitly converted into greyscale,
212  { // check if source is greyscale only
213  bool bIsGrey = true;
214 
215  long nWidth = mpReadAccess->Width();
216  for ( long nY = 0; bIsGrey && ( nY < mpReadAccess->Height() ); nY++ )
217  {
218  BitmapColor aColor;
219  Scanline pScanlineRead = mpReadAccess->GetScanline( nY );
220  for( long nX = 0; bIsGrey && ( nX < nWidth ); nX++ )
221  {
222  aColor = mpReadAccess->HasPalette() ? mpReadAccess->GetPaletteColor( mpReadAccess->GetIndexFromData( pScanlineRead, nX ) )
223  : mpReadAccess->GetPixelFromData( pScanlineRead, nX );
224  bIsGrey = ( aColor.GetRed() == aColor.GetGreen() ) && ( aColor.GetRed() == aColor.GetBlue() );
225  }
226  }
227  if ( bIsGrey )
228  mbGreys = true;
229  }
230  if( mpExpWasGrey )
232 
233  if ( mbGreys )
234  mbNative = ( mpReadAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal && aGraphicBmp.HasGreyPalette());
235  else
237 
238  if( !mbNative )
240 
241  SAL_INFO("vcl", "\nJPEG Export - DPI X: " << rGraphic.GetPPI().getX() << "\nJPEG Export - DPI Y: " << rGraphic.GetPPI().getY());
242 
243  bRet = WriteJPEG( this, &mrStream, mpReadAccess->Width(),
244  mpReadAccess->Height(), rGraphic.GetPPI(), mbGreys,
246 
247  delete[] mpBuffer;
248  mpBuffer = nullptr;
249 
251  }
252  if ( mxStatusIndicator.is() )
253  mxStatusIndicator->end();
254 
255  return bRet;
256 }
257 
258 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt8 GetRed() const
bool WriteJPEG(JPEGWriter *pJPEGWriter, void *pOutputStream, long nWidth, long nHeight, basegfx::B2DSize const &aPPI, bool bGreyScale, long nQualityPercent, long aChromaSubsampling, css::uno::Reference< css::task::XStatusIndicator > const &status)
Definition: jpegc.cxx:366
sal_Int32 mnQuality
Definition: JpegWriter.hxx:37
sal_uInt32 AlignedWidth4Bytes(sal_uInt32 nWidthBits)
Scanline GetScanline(long nY) const
JPEGWriter(SvStream &rStream, const css::uno::Sequence< css::beans::PropertyValue > *pFilterData, bool *pExportWasGrey)
Definition: JpegWriter.cxx:116
bool * mpExpWasGrey
Definition: JpegWriter.hxx:40
double getX() const
double getY() const
void * GetScanline(long nY)
Definition: JpegWriter.cxx:139
#define BUFFER_SIZE
Definition: JpegWriter.cxx:33
long Width() const
sal_Int32 ReadInt32(const OUString &rKey, sal_Int32 nDefault)
static boolean empty_output_buffer(j_compress_ptr cinfo)
Definition: JpegWriter.cxx:60
bool mbNative
Definition: JpegWriter.hxx:35
sal_Int32 maChromaSubsampling
Definition: JpegWriter.hxx:38
sal_uInt8 * mpBuffer
Definition: JpegWriter.hxx:34
sal_uInt8 GetBlue() const
vcl::ScopedBitmapAccess< BitmapReadAccess, Bitmap,&Bitmap::AcquireReadAccess > ScopedReadAccess
Definition: bitmap.hxx:531
sal_uInt8 * Scanline
Definition: Scanline.hxx:25
#define TRUE
bool HasPalette() const
ScanlineFormat GetScanlineFormat() const
static void term_destination(j_compress_ptr cinfo)
Definition: JpegWriter.cxx:75
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
Definition: graph.cxx:349
void jpeg_svstream_dest(j_compress_ptr cinfo, void *output)
Definition: JpegWriter.cxx:92
Reference< XOutputStream > stream
bool mbGreys
Definition: JpegWriter.hxx:36
basegfx::B2DSize GetPPI() const
Definition: graph.cxx:405
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: bitmapex.cxx:236
sal_uInt8 GetGreen() const
long Height() const
unsigned char sal_uInt8
#define SAL_INFO(area, stream)
SvStream & mrStream
Definition: JpegWriter.hxx:32
constexpr::Color COL_WHITE(0xFF, 0xFF, 0xFF)
BitmapColor GetPixelFromData(const sal_uInt8 *pData, long nX) const
Bitmap::ScopedReadAccess mpReadAccess
Definition: JpegWriter.hxx:33
static void init_destination(j_compress_ptr cinfo)
Definition: JpegWriter.cxx:48
const BitmapColor & GetPaletteColor(sal_uInt16 nColor) const
css::uno::Reference< css::task::XStatusIndicator > mxStatusIndicator
Definition: JpegWriter.hxx:42
bool Write(const Graphic &rGraphic)
Definition: JpegWriter.cxx:191
sal_uInt8 GetIndexFromData(const sal_uInt8 *pData, long nX) const