Initial commit.
[pdfium.git] / core / src / fpdfapi / fpdf_edit / fpdf_edit_image.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4  \r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
6 \r
7 #include "../../../include/fpdfapi/fpdf_module.h"\r
8 #include "../../../include/fpdfapi/fpdf_page.h"\r
9 #include "../../../include/fxcodec/fx_codec.h"\r
10 #include "../../../include/fpdfapi/fpdf_render.h"\r
11 #include "../fpdf_page/pageint.h"\r
12 #include "../fpdf_render/render_int.h"\r
13 CPDF_Dictionary* CPDF_Image::InitJPEG(FX_LPBYTE pData, FX_DWORD size)\r
14 {\r
15     FX_INT32 width, height, color_trans, num_comps, bits;\r
16     if (!CPDF_ModuleMgr::Get()->GetJpegModule()->\r
17             LoadInfo(pData, size, width, height, num_comps, bits, color_trans)) {\r
18         return NULL;\r
19     }\r
20     CPDF_Dictionary* pDict = FX_NEW CPDF_Dictionary;\r
21     pDict->SetAtName("Type", "XObject");\r
22     pDict->SetAtName("Subtype", "Image");\r
23     pDict->SetAtInteger("Width", width);\r
24     pDict->SetAtInteger("Height", height);\r
25     FX_LPCSTR csname = NULL;\r
26     if (num_comps == 1) {\r
27         csname = "DeviceGray";\r
28     } else if (num_comps == 3) {\r
29         csname = "DeviceRGB";\r
30     } else if (num_comps == 4) {\r
31         csname = "DeviceCMYK";\r
32         CPDF_Array* pDecode = CPDF_Array::Create();\r
33         for (int n = 0; n < 4; n ++) {\r
34             pDecode->AddInteger(1);\r
35             pDecode->AddInteger(0);\r
36         }\r
37         pDict->SetAt(FX_BSTRC("Decode"), pDecode);\r
38     }\r
39     pDict->SetAtName("ColorSpace", csname);\r
40     pDict->SetAtInteger("BitsPerComponent", bits);\r
41     pDict->SetAtName("Filter", "DCTDecode");\r
42     if (!color_trans) {\r
43         CPDF_Dictionary* pParms = FX_NEW CPDF_Dictionary;\r
44         pDict->SetAt("DecodeParms", pParms);\r
45         pParms->SetAtInteger("ColorTransform", 0);\r
46     }\r
47     m_bIsMask = FALSE;\r
48     m_Width = width;\r
49     m_Height = height;\r
50     if (m_pStream == NULL) {\r
51         m_pStream = FX_NEW CPDF_Stream(NULL, 0, NULL);\r
52     }\r
53     return pDict;\r
54 }\r
55 void CPDF_Image::SetJpegImage(FX_LPBYTE pData, FX_DWORD size)\r
56 {\r
57     CPDF_Dictionary *pDict = InitJPEG(pData, size);\r
58     if (!pDict) {\r
59         return;\r
60     }\r
61     m_pStream->InitStream(pData, size, pDict);\r
62 }\r
63 void CPDF_Image::SetJpegImage(IFX_FileRead *pFile)\r
64 {\r
65     FX_DWORD size = (FX_DWORD)pFile->GetSize();\r
66     if (!size) {\r
67         return;\r
68     }\r
69     FX_DWORD dwEstimateSize = size;\r
70     if (dwEstimateSize > 8192) {\r
71         dwEstimateSize = 8192;\r
72     }\r
73     FX_LPBYTE pData = FX_Alloc(FX_BYTE, dwEstimateSize);\r
74     if (!pData) {\r
75         return;\r
76     }\r
77     pFile->ReadBlock(pData, 0, dwEstimateSize);\r
78     CPDF_Dictionary *pDict = InitJPEG(pData, dwEstimateSize);\r
79     FX_Free(pData);\r
80     if (!pDict && size > dwEstimateSize) {\r
81         pData = FX_Alloc(FX_BYTE, size);\r
82         if (!pData) {\r
83             return;\r
84         }\r
85         pFile->ReadBlock(pData, 0, size);\r
86         pDict = InitJPEG(pData, size);\r
87         FX_Free(pData);\r
88     }\r
89     if (!pDict) {\r
90         return;\r
91     }\r
92     m_pStream->InitStream(pFile, pDict);\r
93 }\r
94 void _DCTEncodeBitmap(CPDF_Dictionary *pBitmapDict, const CFX_DIBitmap* pBitmap, int quality, FX_LPBYTE &buf, FX_STRSIZE &size)\r
95 {\r
96 }\r
97 void _JBIG2EncodeBitmap(CPDF_Dictionary *pBitmapDict, const CFX_DIBitmap *pBitmap, CPDF_Document *pDoc, FX_LPBYTE &buf, FX_STRSIZE &size, FX_BOOL bLossLess)\r
98 {\r
99 }\r
100 void CPDF_Image::SetImage(const CFX_DIBitmap* pBitmap, FX_INT32 iCompress, IFX_FileWrite *pFileWrite, IFX_FileRead *pFileRead, const CFX_DIBitmap* pMask, const CPDF_ImageSetParam* pParam)\r
101 {\r
102     FX_INT32 BitmapWidth = pBitmap->GetWidth();\r
103     FX_INT32 BitmapHeight = pBitmap->GetHeight();\r
104     if (BitmapWidth < 1 || BitmapHeight < 1) {\r
105         return;\r
106     }\r
107     FX_LPBYTE src_buf = pBitmap->GetBuffer();\r
108     FX_INT32 src_pitch = pBitmap->GetPitch();\r
109     FX_INT32 bpp = pBitmap->GetBPP();\r
110     FX_BOOL bUseMatte = pParam && pParam->pMatteColor && (pBitmap->GetFormat() == FXDIB_Argb);\r
111     CPDF_Dictionary* pDict = FX_NEW CPDF_Dictionary;\r
112     pDict->SetAtName(FX_BSTRC("Type"), FX_BSTRC("XObject"));\r
113     pDict->SetAtName(FX_BSTRC("Subtype"), FX_BSTRC("Image"));\r
114     pDict->SetAtInteger(FX_BSTRC("Width"), BitmapWidth);\r
115     pDict->SetAtInteger(FX_BSTRC("Height"), BitmapHeight);\r
116     FX_LPBYTE dest_buf = NULL;\r
117     FX_STRSIZE dest_pitch = 0, dest_size = 0, opType = -1;\r
118     if (bpp == 1) {\r
119         FX_INT32 reset_a = 0, reset_r = 0, reset_g = 0, reset_b = 0;\r
120         FX_INT32 set_a = 0, set_r = 0, set_g = 0, set_b = 0;\r
121         if (!pBitmap->IsAlphaMask()) {\r
122             ArgbDecode(pBitmap->GetPaletteArgb(0), reset_a, reset_r, reset_g, reset_b);\r
123             ArgbDecode(pBitmap->GetPaletteArgb(1), set_a, set_r, set_g, set_b);\r
124         }\r
125         if (set_a == 0 || reset_a == 0) {\r
126             pDict->SetAt(FX_BSTRC("ImageMask"), FX_NEW CPDF_Boolean(TRUE));\r
127             if (reset_a == 0) {\r
128                 CPDF_Array* pArray = FX_NEW CPDF_Array;\r
129                 pArray->AddInteger(1);\r
130                 pArray->AddInteger(0);\r
131                 pDict->SetAt(FX_BSTRC("Decode"), pArray);\r
132             }\r
133         } else {\r
134             CPDF_Array* pCS = FX_NEW CPDF_Array;\r
135             pCS->AddName(FX_BSTRC("Indexed"));\r
136             pCS->AddName(FX_BSTRC("DeviceRGB"));\r
137             pCS->AddInteger(1);\r
138             CFX_ByteString ct;\r
139             FX_LPSTR pBuf = ct.GetBuffer(6);\r
140             pBuf[0] = (FX_CHAR)reset_r;\r
141             pBuf[1] = (FX_CHAR)reset_g;\r
142             pBuf[2] = (FX_CHAR)reset_b;\r
143             pBuf[3] = (FX_CHAR)set_r;\r
144             pBuf[4] = (FX_CHAR)set_g;\r
145             pBuf[5] = (FX_CHAR)set_b;\r
146             ct.ReleaseBuffer(6);\r
147             pCS->Add(CPDF_String::Create(ct, TRUE));\r
148             pDict->SetAt(FX_BSTRC("ColorSpace"), pCS);\r
149         }\r
150         pDict->SetAtInteger(FX_BSTRC("BitsPerComponent"), 1);\r
151         dest_pitch = (BitmapWidth + 7) / 8;\r
152         if ((iCompress & 0x03) == PDF_IMAGE_NO_COMPRESS) {\r
153             opType = 1;\r
154         } else {\r
155             opType = 0;\r
156         }\r
157     } else if (bpp == 8) {\r
158         FX_INT32 iPalette = pBitmap->GetPaletteSize();\r
159         if (iPalette > 0) {\r
160             CPDF_Array* pCS = FX_NEW CPDF_Array;\r
161             m_pDocument->AddIndirectObject(pCS);\r
162             pCS->AddName(FX_BSTRC("Indexed"));\r
163             pCS->AddName(FX_BSTRC("DeviceRGB"));\r
164             pCS->AddInteger(iPalette - 1);\r
165             FX_LPBYTE pColorTable = FX_Alloc(FX_BYTE, iPalette * 3);\r
166             FX_LPBYTE ptr = pColorTable;\r
167             for (FX_INT32 i = 0; i < iPalette; i ++) {\r
168                 FX_DWORD argb = pBitmap->GetPaletteArgb(i);\r
169                 ptr[0] = (FX_BYTE)(argb >> 16);\r
170                 ptr[1] = (FX_BYTE)(argb >> 8);\r
171                 ptr[2] = (FX_BYTE)argb;\r
172                 ptr += 3;\r
173             }\r
174             CPDF_Stream *pCTS = CPDF_Stream::Create(pColorTable, iPalette * 3, CPDF_Dictionary::Create());\r
175             m_pDocument->AddIndirectObject(pCTS);\r
176             pCS->AddReference(m_pDocument, pCTS);\r
177             pDict->SetAtReference(FX_BSTRC("ColorSpace"), m_pDocument, pCS);\r
178         } else {\r
179             pDict->SetAtName(FX_BSTRC("ColorSpace"), FX_BSTRC("DeviceGray"));\r
180         }\r
181         pDict->SetAtInteger(FX_BSTRC("BitsPerComponent"), 8);\r
182         if ((iCompress & 0x03) == PDF_IMAGE_NO_COMPRESS) {\r
183             dest_pitch = BitmapWidth;\r
184             opType = 1;\r
185         } else {\r
186             opType = 0;\r
187         }\r
188     } else {\r
189         pDict->SetAtName(FX_BSTRC("ColorSpace"), FX_BSTRC("DeviceRGB"));\r
190         pDict->SetAtInteger(FX_BSTRC("BitsPerComponent"), 8);\r
191         if ((iCompress & 0x03) == PDF_IMAGE_NO_COMPRESS) {\r
192             dest_pitch = BitmapWidth * 3;\r
193             opType = 2;\r
194         } else {\r
195             opType = 0;\r
196         }\r
197     }\r
198     const CFX_DIBitmap* pMaskBitmap = NULL;\r
199     if (pBitmap->HasAlpha()) {\r
200         pMaskBitmap = pBitmap->GetAlphaMask();\r
201     }\r
202     if (!pMaskBitmap && pMask) {\r
203         FXDIB_Format maskFormat = pMask->GetFormat();\r
204         if (maskFormat == FXDIB_1bppMask || maskFormat == FXDIB_8bppMask) {\r
205             pMaskBitmap = pMask;\r
206         }\r
207     }\r
208     if (pMaskBitmap) {\r
209         FX_INT32 maskWidth = pMaskBitmap->GetWidth();\r
210         FX_INT32 maskHeight = pMaskBitmap->GetHeight();\r
211         FX_LPBYTE mask_buf = NULL;\r
212         FX_STRSIZE mask_size;\r
213         FX_BOOL bDeleteMask = TRUE;\r
214         CPDF_Dictionary* pMaskDict = FX_NEW CPDF_Dictionary;\r
215         pMaskDict->SetAtName(FX_BSTRC("Type"), FX_BSTRC("XObject"));\r
216         pMaskDict->SetAtName(FX_BSTRC("Subtype"), FX_BSTRC("Image"));\r
217         pMaskDict->SetAtInteger(FX_BSTRC("Width"), maskWidth);\r
218         pMaskDict->SetAtInteger(FX_BSTRC("Height"), maskHeight);\r
219         pMaskDict->SetAtName(FX_BSTRC("ColorSpace"), FX_BSTRC("DeviceGray"));\r
220         pMaskDict->SetAtInteger(FX_BSTRC("BitsPerComponent"), 8);\r
221         if (pMaskBitmap->GetBPP() == 8 && (iCompress & PDF_IMAGE_MASK_LOSSY_COMPRESS) != 0) {\r
222             _DCTEncodeBitmap(pMaskDict, pMaskBitmap, pParam ? pParam->nQuality : 75, mask_buf, mask_size);\r
223         } else if (pMaskBitmap->GetFormat() == FXDIB_1bppMask) {\r
224             _JBIG2EncodeBitmap(pMaskDict, pMaskBitmap, m_pDocument, mask_buf, mask_size, TRUE);\r
225         } else {\r
226             mask_size = maskHeight * maskWidth;\r
227             mask_buf = FX_Alloc(FX_BYTE, mask_size);\r
228             for (FX_INT32 a = 0; a < maskHeight; a ++) {\r
229                 FXSYS_memcpy32(mask_buf + a * maskWidth, pMaskBitmap->GetScanline(a), maskWidth);\r
230             }\r
231         }\r
232         if (pMaskDict) {\r
233             pMaskDict->SetAtInteger(FX_BSTRC("Length"), mask_size);\r
234             CPDF_Stream* pMaskStream = NULL;\r
235             if (bUseMatte) {\r
236                 int a, r, g, b;\r
237                 ArgbDecode(*(pParam->pMatteColor), a, r, g, b);\r
238                 CPDF_Array* pMatte = FX_NEW CPDF_Array;\r
239                 pMatte->AddInteger(r);\r
240                 pMatte->AddInteger(g);\r
241                 pMatte->AddInteger(b);\r
242                 pMaskDict->SetAt(FX_BSTRC("Matte"), pMatte);\r
243             }\r
244             pMaskStream = FX_NEW CPDF_Stream(mask_buf, mask_size, pMaskDict);\r
245             m_pDocument->AddIndirectObject(pMaskStream);\r
246             bDeleteMask = FALSE;\r
247             pDict->SetAtReference(FX_BSTRC("SMask"), m_pDocument, pMaskStream);\r
248         }\r
249         if (pBitmap->HasAlpha()) {\r
250             delete pMaskBitmap;\r
251         }\r
252     }\r
253     FX_BOOL bStream = pFileWrite != NULL && pFileRead != NULL;\r
254     if (opType == 0) {\r
255         if (iCompress & PDF_IMAGE_LOSSLESS_COMPRESS) {\r
256             if (pBitmap->GetBPP() == 1) {\r
257                 _JBIG2EncodeBitmap(pDict, pBitmap, m_pDocument, dest_buf, dest_size, TRUE);\r
258             }\r
259         } else {\r
260             if (pBitmap->GetBPP() == 1) {\r
261                 _JBIG2EncodeBitmap(pDict, pBitmap, m_pDocument, dest_buf, dest_size, FALSE);\r
262             } else if (pBitmap->GetBPP() >= 8 && pBitmap->GetPalette() != NULL) {\r
263                 CFX_DIBitmap *pNewBitmap = FX_NEW CFX_DIBitmap();\r
264                 pNewBitmap->Copy(pBitmap);\r
265                 pNewBitmap->ConvertFormat(FXDIB_Rgb);\r
266                 SetImage(pNewBitmap, iCompress, pFileWrite, pFileRead);\r
267                 if (pDict) {\r
268                     pDict->Release();\r
269                     pDict = NULL;\r
270                 }\r
271                 if (dest_buf) {\r
272                     FX_Free(dest_buf);\r
273                     dest_buf = NULL;\r
274                 }\r
275                 dest_size = 0;\r
276                 delete pNewBitmap;\r
277                 return;\r
278             } else {\r
279                 if (bUseMatte) {\r
280                     CFX_DIBitmap *pNewBitmap = FX_NEW CFX_DIBitmap();\r
281                     pNewBitmap->Create(BitmapWidth, BitmapHeight, FXDIB_Argb);\r
282                     FX_LPBYTE dst_buf = pNewBitmap->GetBuffer();\r
283                     FX_INT32 src_offset = 0;\r
284                     for (FX_INT32 row = 0; row < BitmapHeight; row ++) {\r
285                         src_offset = row * src_pitch;\r
286                         for (FX_INT32 column = 0; column < BitmapWidth; column ++) {\r
287                             FX_FLOAT alpha = src_buf[src_offset + 3] / 255.0f;\r
288                             dst_buf[src_offset] = (FX_BYTE)(src_buf[src_offset] * alpha);\r
289                             dst_buf[src_offset + 1] = (FX_BYTE)(src_buf[src_offset + 1] * alpha);\r
290                             dst_buf[src_offset + 2] = (FX_BYTE)(src_buf[src_offset + 2] * alpha);\r
291                             dst_buf[src_offset + 3] = (FX_BYTE)(src_buf[src_offset + 3]);\r
292                             src_offset += 4;\r
293                         }\r
294                     }\r
295                     _DCTEncodeBitmap(pDict, pNewBitmap, pParam ? pParam->nQuality : 75, dest_buf, dest_size);\r
296                     delete pNewBitmap;\r
297                 } else {\r
298                     _DCTEncodeBitmap(pDict, pBitmap, pParam ? pParam->nQuality : 75, dest_buf, dest_size);\r
299                 }\r
300             }\r
301         }\r
302         if (bStream) {\r
303             pFileWrite->WriteBlock(dest_buf, dest_size);\r
304             FX_Free(dest_buf);\r
305             dest_buf = NULL;\r
306         }\r
307     } else if (opType == 1) {\r
308         if (!bStream) {\r
309             dest_size = dest_pitch * BitmapHeight;\r
310             dest_buf = FX_Alloc(FX_BYTE, dest_size);\r
311         }\r
312         FX_LPBYTE pDest = dest_buf;\r
313         for (FX_INT32 i = 0; i < BitmapHeight; i ++) {\r
314             if (!bStream) {\r
315                 FXSYS_memcpy32(pDest, src_buf, dest_pitch);\r
316                 pDest += dest_pitch;\r
317             } else {\r
318                 pFileWrite->WriteBlock(src_buf, dest_pitch);\r
319             }\r
320             src_buf += src_pitch;\r
321         }\r
322     } else if (opType == 2) {\r
323         if (!bStream) {\r
324             dest_size = dest_pitch * BitmapHeight;\r
325             dest_buf = FX_Alloc(FX_BYTE, dest_size);\r
326         } else {\r
327             dest_buf = FX_Alloc(FX_BYTE, dest_pitch);\r
328         }\r
329         FX_LPBYTE pDest = dest_buf;\r
330         FX_INT32 src_offset = 0;\r
331         FX_INT32 dest_offset = 0;\r
332         for (FX_INT32 row = 0; row < BitmapHeight; row ++) {\r
333             src_offset = row * src_pitch;\r
334             for (FX_INT32 column = 0; column < BitmapWidth; column ++) {\r
335                 FX_FLOAT alpha = bUseMatte ? src_buf[src_offset + 3] / 255.0f : 1;\r
336                 pDest[dest_offset] = (FX_BYTE)(src_buf[src_offset + 2] * alpha);\r
337                 pDest[dest_offset + 1] = (FX_BYTE)(src_buf[src_offset + 1] * alpha);\r
338                 pDest[dest_offset + 2] = (FX_BYTE)(src_buf[src_offset] * alpha);\r
339                 dest_offset += 3;\r
340                 src_offset += bpp == 24 ? 3 : 4;\r
341             }\r
342             if (bStream) {\r
343                 pFileWrite->WriteBlock(pDest, dest_pitch);\r
344                 pDest = dest_buf;\r
345             } else {\r
346                 pDest += dest_pitch;\r
347             }\r
348             dest_offset = 0;\r
349         }\r
350         if (bStream) {\r
351             FX_Free(dest_buf);\r
352             dest_buf = NULL;\r
353         }\r
354     }\r
355     if (m_pStream == NULL) {\r
356         m_pStream = FX_NEW CPDF_Stream(NULL, 0, NULL);\r
357     }\r
358     if (!bStream) {\r
359         m_pStream->InitStream(dest_buf, dest_size, pDict);\r
360     } else {\r
361         pFileWrite->Flush();\r
362         m_pStream->InitStream(pFileRead, pDict);\r
363     }\r
364     m_bIsMask = pBitmap->IsAlphaMask();\r
365     m_Width = BitmapWidth;\r
366     m_Height = BitmapHeight;\r
367     if (dest_buf) {\r
368         FX_Free(dest_buf);\r
369     }\r
370 }\r
371 void CPDF_Image::ResetCache(CPDF_Page* pPage, const CFX_DIBitmap* pBitmap)\r
372 {\r
373     pPage->GetRenderCache()->ResetBitmap(m_pStream, pBitmap);\r
374 }\r