blob: 65b9b245140a82182aaeb9b37b766a0ad6107e36 [file] [log] [blame]
/*
* Copyright (C) 2010 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 "SkPDFImage.h"
#include "SkBitmap.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkPaint.h"
#include "SkPackBits.h"
#include "SkPDFCatalog.h"
#include "SkStream.h"
#include "SkString.h"
#include "SkUnPreMultiply.h"
namespace {
SkMemoryStream* extractImageData(const SkBitmap& bitmap) {
SkMemoryStream* result;
bitmap.lockPixels();
switch (bitmap.getConfig()) {
case SkBitmap::kIndex8_Config:
result = new SkMemoryStream(bitmap.getPixels(), bitmap.getSize(),
true);
break;
case SkBitmap::kRLE_Index8_Config: {
result = new SkMemoryStream(bitmap.getSize());
const SkBitmap::RLEPixels* rle =
(const SkBitmap::RLEPixels*)bitmap.getPixels();
uint8_t* dst = (uint8_t*)result->getMemoryBase();
const int width = bitmap.width();
for (int y = 0; y < bitmap.height(); y++) {
SkPackBits::Unpack8(rle->packedAtY(y), width, dst);
dst += width;
}
break;
}
case SkBitmap::kARGB_4444_Config: {
const int width = bitmap.width();
const int rowBytes = (width * 3 + 1) / 2;
result = new SkMemoryStream(rowBytes * bitmap.height());
uint8_t* dst = (uint8_t*)result->getMemoryBase();
for (int y = 0; y < bitmap.height(); y++) {
uint16_t* src = bitmap.getAddr16(0, y);
for (int x = 0; x < width; x += 2) {
dst[0] = (SkGetPackedR4444(src[0]) << 4) |
SkGetPackedG4444(src[0]);
dst[1] = (SkGetPackedB4444(src[0]) << 4) |
SkGetPackedR4444(src[1]);
dst[2] = (SkGetPackedG4444(src[1]) << 4) |
SkGetPackedB4444(src[1]);
src += 2;
dst += 3;
}
if (width & 1) {
dst[0] = (SkGetPackedR4444(src[0]) << 4) |
SkGetPackedG4444(src[0]);
dst[1] = (SkGetPackedB4444(src[0]) << 4);
}
}
break;
}
case SkBitmap::kRGB_565_Config: {
const int width = bitmap.width();
const int rowBytes = width * 3;
result = new SkMemoryStream(rowBytes * bitmap.height());
uint8_t* dst = (uint8_t*)result->getMemoryBase();
for (int y = 0; y < bitmap.height(); y++) {
uint16_t* src = bitmap.getAddr16(0, y);
for (int x = 0; x < width; x++) {
dst[0] = SkGetPackedR16(src[0]);
dst[1] = SkGetPackedG16(src[0]);
dst[2] = SkGetPackedB16(src[0]);
src++;
dst += 3;
}
}
break;
}
case SkBitmap::kARGB_8888_Config: {
const int width = bitmap.width();
const int rowBytes = width * 3;
result = new SkMemoryStream(rowBytes * bitmap.height());
uint8_t* dst = (uint8_t*)result->getMemoryBase();
for (int y = 0; y < bitmap.height(); y++) {
uint32_t* src = bitmap.getAddr32(0, y);
for (int x = 0; x < width; x++) {
dst[0] = SkGetPackedR32(src[0]);
dst[1] = SkGetPackedG32(src[0]);
dst[2] = SkGetPackedB32(src[0]);
src++;
dst += 3;
}
}
break;
}
default:
SkASSERT(false);
}
bitmap.unlockPixels();
return result;
}
SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
SkPDFArray* result = new SkPDFArray();
result->reserve(4);
SkRefPtr<SkPDFName> indexedName = new SkPDFName("Indexed");
indexedName->unref(); // SkRefPtr and new both took a reference.
result->append(indexedName.get());
SkRefPtr<SkPDFName> rgbName = new SkPDFName("DeviceRGB");
rgbName->unref(); // SkRefPtr and new both took a reference.
result->append(rgbName.get());
rgbName->unref(); // SkRefPtr and new both took a reference.
SkRefPtr<SkPDFInt> countValue = new SkPDFInt(table->count() - 1);
result->append(countValue.get());
// Potentially, this could be represented in fewer bytes with a stream.
// Max size as a string is 1.5k.
SkString index;
for (int i = 0; i < table->count(); i++) {
char buf[3];
SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
buf[0] = SkGetPackedR32(color);
buf[1] = SkGetPackedG32(color);
buf[2] = SkGetPackedB32(color);
index.append(buf, 3);
}
SkRefPtr<SkPDFString> indexValue = new SkPDFString(index);
indexValue->unref(); // SkRefPtr and new both took a reference.
result->append(indexValue.get());
return result;
}
}; // namespace
SkPDFImage::SkPDFImage(const SkBitmap& bitmap, const SkPaint& paint) {
SkBitmap::Config config = bitmap.getConfig();
// TODO(vandebo) Handle alpha and alpha only images correctly.
SkASSERT(config == SkBitmap::kRGB_565_Config ||
config == SkBitmap::kARGB_4444_Config ||
config == SkBitmap::kARGB_8888_Config ||
config == SkBitmap::kIndex8_Config ||
config == SkBitmap::kRLE_Index8_Config);
SkMemoryStream* image_data = extractImageData(bitmap);
SkAutoUnref image_data_unref(image_data);
fStream = new SkPDFStream(image_data);
fStream->unref(); // SkRefPtr and new both took a reference.
SkRefPtr<SkPDFName> typeValue = new SkPDFName("XObject");
typeValue->unref(); // SkRefPtr and new both took a reference.
insert("Type", typeValue.get());
SkRefPtr<SkPDFName> subTypeValue = new SkPDFName("Image");
subTypeValue->unref(); // SkRefPtr and new both took a reference.
insert("Subtype", subTypeValue.get());
SkRefPtr<SkPDFInt> widthValue = new SkPDFInt(bitmap.width());
widthValue->unref(); // SkRefPtr and new both took a reference.
insert("Width", widthValue.get());
SkRefPtr<SkPDFInt> heightValue = new SkPDFInt(bitmap.height());
heightValue->unref(); // SkRefPtr and new both took a reference.
insert("Height", heightValue.get());
// if (!image mask) {
SkRefPtr<SkPDFObject> colorSpaceValue;
if (config == SkBitmap::kIndex8_Config ||
config == SkBitmap::kRLE_Index8_Config) {
colorSpaceValue = makeIndexedColorSpace(bitmap.getColorTable());
} else {
colorSpaceValue = new SkPDFName("DeviceRGB");
}
colorSpaceValue->unref(); // SkRefPtr and new both took a reference.
insert("ColorSpace", colorSpaceValue.get());
// }
int bitsPerComp = bitmap.bytesPerPixel() * 2;
if (bitsPerComp == 0) {
SkASSERT(config == SkBitmap::kA1_Config);
bitsPerComp = 1;
} else if (bitsPerComp == 2 ||
(bitsPerComp == 4 && config == SkBitmap::kRGB_565_Config)) {
bitsPerComp = 8;
}
SkRefPtr<SkPDFInt> bitsPerCompValue = new SkPDFInt(bitsPerComp);
bitsPerCompValue->unref(); // SkRefPtr and new both took a reference.
insert("BitsPerComponent", bitsPerCompValue.get());
if (config == SkBitmap::kRGB_565_Config) {
SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
zeroVal->unref(); // SkRefPtr and new both took a reference.
SkRefPtr<SkPDFScalar> scale5Val = new SkPDFScalar(8.2258); // 255/2^5-1
scale5Val->unref(); // SkRefPtr and new both took a reference.
SkRefPtr<SkPDFScalar> scale6Val = new SkPDFScalar(4.0476); // 255/2^6-1
scale6Val->unref(); // SkRefPtr and new both took a reference.
SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
decodeValue->unref(); // SkRefPtr and new both took a reference.
decodeValue->reserve(6);
decodeValue->append(zeroVal.get());
decodeValue->append(scale5Val.get());
decodeValue->append(zeroVal.get());
decodeValue->append(scale6Val.get());
decodeValue->append(zeroVal.get());
decodeValue->append(scale5Val.get());
insert("Decode", decodeValue.get());
}
}
SkPDFImage::~SkPDFImage() {}
void SkPDFImage::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect) {
if (indirect)
return emitIndirectObject(stream, catalog);
fStream->emitObject(stream, catalog, indirect);
}
size_t SkPDFImage::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
if (indirect)
return getIndirectOutputSize(catalog);
return fStream->getOutputSize(catalog, indirect);
}
void SkPDFImage::insert(SkPDFName* key, SkPDFObject* value) {
fStream->insert(key, value);
}
void SkPDFImage::insert(const char key[], SkPDFObject* value) {
fStream->insert(key, value);
}