/*
* Copyright 2007, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SkImageDecoder.h"
#include "SkScaledBitmapSampler.h"
#include "SkStream.h"
#include "SkColorPriv.h"
#include "SkTDArray.h"
#include "fpdfemb.h"
class SkFPDFEMBImageDecoder : public SkImageDecoder {
public:
SkFPDFEMBImageDecoder() {}
virtual Format getFormat() const {
return kBMP_Format;
}
protected:
virtual bool onDecode(SkStream* stream, SkBitmap* bm,
SkBitmap::Config pref, Mode mode);
private:
bool render(FPDFEMB_PAGE page, const FPDFEMB_RECT& bounds, SkBitmap* bm,
SkBitmap::Config prefConfig, SkImageDecoder::Mode mode);
};
SkImageDecoder* SkImageDecoder_FPDFEMB_Factory(SkStream*);
SkImageDecoder* SkImageDecoder_FPDFEMB_Factory(SkStream* stream) {
static const char kPDFSig[] = { '%', 'P', 'D', 'F' };
size_t len = stream->getLength();
char buffer[sizeof(kPDFSig)];
SkDebugf("---- SkImageDecoder_FPDFEMB_Factory len=%d\n", len);
if (len != 12683) { return NULL; }
if (len > sizeof(kPDFSig) &&
stream->read(buffer, sizeof(kPDFSig)) == sizeof(kPDFSig) &&
!memcmp(buffer, kPDFSig, sizeof(kPDFSig))) {
return SkNEW(SkFPDFEMBImageDecoder);
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
extern "C" {
static void* pdf_alloc(FPDFEMB_MEMMGR* pMgr, unsigned int size) {
void* addr = sk_malloc_throw(size);
// SkDebugf("---- pdf_alloc %d %p\n", size, addr);
return addr;
}
static void* pdf_alloc_nl(FPDFEMB_MEMMGR* pMgr, unsigned int size) {
void* addr = sk_malloc_flags(size, 0);
// SkDebugf("---- pdf_alloc_nl %d %p\n", size, addr);
return addr;
}
static void* pdf_realloc(FPDFEMB_MEMMGR*, void* addr, unsigned int size) {
void* newaddr = sk_realloc_throw(addr, size);
// SkDebugf("---- pdf_realloc %p %d %p\n", addr, size, newaddr);
return newaddr;
}
static void pdf_free(FPDFEMB_MEMMGR* pMgr, void* pointer) {
// SkDebugf("---- pdf_free %p\n", pointer);
sk_free(pointer);
}
void FX_OUTPUT_LOG_FUNC(const char* format, ...) {
SkDebugf("---- LOG_FUNC %s\n", format);
}
static unsigned int file_getsize(FPDFEMB_FILE_ACCESS* file) {
SkStream* stream = (SkStream*)file->user;
return stream->getLength();
}
static FPDFEMB_RESULT file_readblock(FPDFEMB_FILE_ACCESS* file, void* dst,
unsigned int offset, unsigned int size) {
SkStream* stream = (SkStream*)file->user;
// SkDebugf("---- readblock %p %p %d %d\n", stream, dst, offset, size);
if (!stream->rewind()) {
SkDebugf("---- rewind failed\n");
return FPDFERR_ERROR;
}
if (stream->skip(offset) != offset) {
SkDebugf("---- skip failed\n");
return FPDFERR_ERROR;
}
if (stream->read(dst, size) != size) {
SkDebugf("---- read failed\n");
return FPDFERR_ERROR;
}
return FPDFERR_SUCCESS;
}
static void pdf_oom_handler(void* memory, int size) {
SkDebugf("======== pdf OOM %p %d\n", memory, size);
}
}
static inline int PDF2Pixels(int x) { return x / 100; }
static inline SkScalar PDF2Scalar(int x) {
return SkScalarMulDiv(SK_Scalar1, x, 100);
}
bool SkFPDFEMBImageDecoder::render(FPDFEMB_PAGE page, const FPDFEMB_RECT& bounds, SkBitmap* bm,
SkBitmap::Config prefConfig, SkImageDecoder::Mode mode) {
int width = PDF2Pixels(bounds.right - bounds.left);
int height = PDF2Pixels(bounds.top - bounds.bottom);
SkDebugf("----- bitmap size [%d %d], mode=%d\n", width, height, mode);
bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
if (SkImageDecoder::kDecodeBounds_Mode == mode) {
return true;
}
// USE THE CODEC TO ALLOCATE THE PIXELS!!!!
if (!this->allocPixelRef(bm, NULL)) {
SkDebugf("----- failed to alloc pixels\n");
return false;
}
bm->eraseColor(0);
FPDFEMB_RESULT result;
FPDFEMB_BITMAP dib;
result = FPDFEMB_CreateDIB(width, height, FPDFDIB_BGRA, bm->getPixels(),
bm->rowBytes(), &dib);
SkDebugf("---- createdib %d\n", result);
result = FPDFEMB_StartRender(dib, page, 0, 0, width, height, 0, 0, NULL, NULL);
SkDebugf("---- render %d\n", result);
result = FPDFEMB_DestroyDIB(dib);
SkDebugf("---- destroydib %d\n", result);
SkPMColor* dst = bm->getAddr32(0, 0);
const uint8_t* src = (uint8_t*)dst;
int n = bm->getSize() >> 2;
for (int i = 0; i < n; i++) {
int b = *src++;
int g = *src++;
int r = *src++;
int a = *src++;
*dst++ = SkPackARGB32(a, r, g, b);
}
return true;
}
#define USE_FIXED_MEM (4 * 1024 * 1024)
bool SkFPDFEMBImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
SkBitmap::Config prefConfig, Mode mode) {
FPDFEMB_RESULT result;
#ifdef USE_FIXED_MEM
SkAutoMalloc storage(USE_FIXED_MEM);
result = FPDFEMB_InitFixedMemory(storage.get(), USE_FIXED_MEM,
pdf_oom_handler);
#else
FPDFEMB_MEMMGR memmgr;
memmgr.Alloc = pdf_alloc;
memmgr.AllocNL = pdf_alloc_nl;
memmgr.Realloc = pdf_realloc;
memmgr.Free = pdf_free;
result = FPDFEMB_Init(&memmgr);
#endif
SkDebugf("----- SkImageDecoder_FPDFEMB_Factory init %d, streamLen = %d\n", result, stream->getLength());
FPDFEMB_FILE_ACCESS file;
file.GetSize = file_getsize;
file.ReadBlock = file_readblock;
file.user = stream;
FPDFEMB_DOCUMENT document;
result = FPDFEMB_StartLoadDocument(&file, NULL, &document, NULL);
SkDebugf("----- SkImageDecoder_FPDFEMB_Factory open %d %p\n", result, document);
int pageCount = FPDFEMB_GetPageCount(document);
SkDebugf("----- SkImageDecoder_FPDFEMB_Factory pageCount %d\n", pageCount);
if (pageCount > 0) {
FPDFEMB_PAGE page;
result = FPDFEMB_LoadPage(document, 0, &page);
SkDebugf("----- SkImageDecoder_FPDFEMB_Factory load page %d\n", result);
int width, height;
result = FPDFEMB_GetPageSize(page, &width, &height);
SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page size %d [%d %d]\n", result, width, height);
FPDFEMB_RECT rect;
result = FPDFEMB_GetPageBBox(page, &rect);
SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page rect %d [%d %d %d %d]\n", result,
rect.left, rect.top, rect.right, rect.bottom);
SkDebugf("----- SkImageDecoder_FPDFEMB_Factory begin page parse...\n");
result = FPDFEMB_StartParse(page, false, NULL);
SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page parse %d\n", result);
if (0 == result) {
this->render(page, rect, bm, prefConfig, mode);
}
result = FPDFEMB_ClosePage(page);
SkDebugf("----- SkImageDecoder_FPDFEMB_Factory close page %d\n", result);
}
result = FPDFEMB_CloseDocument(document);
SkDebugf("----- SkImageDecoder_FPDFEMB_Factory close %d\n", result);
// FPDFEMB_Exit();
return true;
}