1/* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "src/pdf/SkPDFBitmap.h" 9 10#include "include/core/SkBitmap.h" 11#include "include/core/SkData.h" 12#include "include/core/SkExecutor.h" 13#include "include/core/SkImage.h" 14#include "include/core/SkStream.h" 15#include "include/private/SkColorData.h" 16#include "include/private/SkImageInfoPriv.h" 17#include "include/private/SkTo.h" 18#include "src/pdf/SkDeflate.h" 19#include "src/pdf/SkJpegInfo.h" 20#include "src/pdf/SkPDFDocumentPriv.h" 21#include "src/pdf/SkPDFTypes.h" 22#include "src/pdf/SkPDFUtils.h" 23 24//////////////////////////////////////////////////////////////////////////////// 25 26// write a single byte to a stream n times. 27static void fill_stream(SkWStream* out, char value, size_t n) { 28 char buffer[4096]; 29 memset(buffer, value, sizeof(buffer)); 30 for (size_t i = 0; i < n / sizeof(buffer); ++i) { 31 out->write(buffer, sizeof(buffer)); 32 } 33 out->write(buffer, n % sizeof(buffer)); 34} 35 36/* It is necessary to average the color component of transparent 37 pixels with their surrounding neighbors since the PDF renderer may 38 separately re-sample the alpha and color channels when the image is 39 not displayed at its native resolution. Since an alpha of zero 40 gives no information about the color component, the pathological 41 case is a white image with sharp transparency bounds - the color 42 channel goes to black, and the should-be-transparent pixels are 43 rendered as grey because of the separate soft mask and color 44 resizing. e.g.: gm/bitmappremul.cpp */ 45static SkColor get_neighbor_avg_color(const SkPixmap& bm, int xOrig, int yOrig) { 46 SkASSERT(kBGRA_8888_SkColorType == bm.colorType()); 47 unsigned r = 0, g = 0, b = 0, n = 0; 48 // Clamp the range to the edge of the bitmap. 49 int ymin = std::max(0, yOrig - 1); 50 int ymax = std::min(yOrig + 1, bm.height() - 1); 51 int xmin = std::max(0, xOrig - 1); 52 int xmax = std::min(xOrig + 1, bm.width() - 1); 53 for (int y = ymin; y <= ymax; ++y) { 54 const SkColor* scanline = bm.addr32(0, y); 55 for (int x = xmin; x <= xmax; ++x) { 56 SkColor color = scanline[x]; 57 if (color != SK_ColorTRANSPARENT) { 58 r += SkColorGetR(color); 59 g += SkColorGetG(color); 60 b += SkColorGetB(color); 61 n++; 62 } 63 } 64 } 65 return n > 0 ? SkColorSetRGB(SkToU8(r / n), SkToU8(g / n), SkToU8(b / n)) 66 : SK_ColorTRANSPARENT; 67} 68 69template <typename T> 70static void emit_image_stream(SkPDFDocument* doc, 71 SkPDFIndirectReference ref, 72 T writeStream, 73 SkISize size, 74 const char* colorSpace, 75 SkPDFIndirectReference sMask, 76 int length, 77 bool isJpeg) { 78 SkPDFDict pdfDict("XObject"); 79 pdfDict.insertName("Subtype", "Image"); 80 pdfDict.insertInt("Width", size.width()); 81 pdfDict.insertInt("Height", size.height()); 82 pdfDict.insertName("ColorSpace", colorSpace); 83 if (sMask) { 84 pdfDict.insertRef("SMask", sMask); 85 } 86 pdfDict.insertInt("BitsPerComponent", 8); 87 #ifdef SK_PDF_BASE85_BINARY 88 auto filters = SkPDFMakeArray(); 89 filters->appendName("ASCII85Decode"); 90 filters->appendName(isJpeg ? "DCTDecode" : "FlateDecode"); 91 pdfDict.insertObject("Filter", std::move(filters)); 92 #else 93 pdfDict.insertName("Filter", isJpeg ? "DCTDecode" : "FlateDecode"); 94 #endif 95 if (isJpeg) { 96 pdfDict.insertInt("ColorTransform", 0); 97 } 98 pdfDict.insertInt("Length", length); 99 doc->emitStream(pdfDict, std::move(writeStream), ref); 100} 101 102static void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectReference ref) { 103 SkDynamicMemoryWStream buffer; 104 SkDeflateWStream deflateWStream(&buffer); 105 if (kAlpha_8_SkColorType == pm.colorType()) { 106 SkASSERT(pm.rowBytes() == (size_t)pm.width()); 107 buffer.write(pm.addr8(), pm.width() * pm.height()); 108 } else { 109 SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType); 110 SkASSERT(pm.colorType() == kBGRA_8888_SkColorType); 111 SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4); 112 const uint32_t* ptr = pm.addr32(); 113 const uint32_t* stop = ptr + pm.height() * pm.width(); 114 115 uint8_t byteBuffer[4092]; 116 uint8_t* bufferStop = byteBuffer + SK_ARRAY_COUNT(byteBuffer); 117 uint8_t* dst = byteBuffer; 118 while (ptr != stop) { 119 *dst++ = 0xFF & ((*ptr++) >> SK_BGRA_A32_SHIFT); 120 if (dst == bufferStop) { 121 deflateWStream.write(byteBuffer, sizeof(byteBuffer)); 122 dst = byteBuffer; 123 } 124 } 125 deflateWStream.write(byteBuffer, dst - byteBuffer); 126 } 127 deflateWStream.finalize(); 128 129 #ifdef SK_PDF_BASE85_BINARY 130 SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer); 131 #endif 132 int length = SkToInt(buffer.bytesWritten()); 133 emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); }, 134 pm.info().dimensions(), "DeviceGray", SkPDFIndirectReference(), 135 length, false); 136} 137 138static void do_deflated_image(const SkPixmap& pm, 139 SkPDFDocument* doc, 140 bool isOpaque, 141 SkPDFIndirectReference ref) { 142 SkPDFIndirectReference sMask; 143 if (!isOpaque) { 144 sMask = doc->reserveRef(); 145 } 146 SkDynamicMemoryWStream buffer; 147 SkDeflateWStream deflateWStream(&buffer); 148 const char* colorSpace = "DeviceGray"; 149 switch (pm.colorType()) { 150 case kAlpha_8_SkColorType: 151 fill_stream(&deflateWStream, '\x00', pm.width() * pm.height()); 152 break; 153 case kGray_8_SkColorType: 154 SkASSERT(sMask.fValue = -1); 155 SkASSERT(pm.rowBytes() == (size_t)pm.width()); 156 deflateWStream.write(pm.addr8(), pm.width() * pm.height()); 157 break; 158 default: 159 colorSpace = "DeviceRGB"; 160 SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType); 161 SkASSERT(pm.colorType() == kBGRA_8888_SkColorType); 162 SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4); 163 uint8_t byteBuffer[3072]; 164 static_assert(SK_ARRAY_COUNT(byteBuffer) % 3 == 0, ""); 165 uint8_t* bufferStop = byteBuffer + SK_ARRAY_COUNT(byteBuffer); 166 uint8_t* dst = byteBuffer; 167 for (int y = 0; y < pm.height(); ++y) { 168 const SkColor* src = pm.addr32(0, y); 169 for (int x = 0; x < pm.width(); ++x) { 170 SkColor color = *src++; 171 if (SkColorGetA(color) == SK_AlphaTRANSPARENT) { 172 color = get_neighbor_avg_color(pm, x, y); 173 } 174 *dst++ = SkColorGetR(color); 175 *dst++ = SkColorGetG(color); 176 *dst++ = SkColorGetB(color); 177 if (dst == bufferStop) { 178 deflateWStream.write(byteBuffer, sizeof(byteBuffer)); 179 dst = byteBuffer; 180 } 181 } 182 } 183 deflateWStream.write(byteBuffer, dst - byteBuffer); 184 } 185 deflateWStream.finalize(); 186 #ifdef SK_PDF_BASE85_BINARY 187 SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer); 188 #endif 189 int length = SkToInt(buffer.bytesWritten()); 190 emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); }, 191 pm.info().dimensions(), colorSpace, sMask, length, false); 192 if (!isOpaque) { 193 do_deflated_alpha(pm, doc, sMask); 194 } 195} 196 197static bool do_jpeg(sk_sp<SkData> data, SkPDFDocument* doc, SkISize size, 198 SkPDFIndirectReference ref) { 199 SkISize jpegSize; 200 SkEncodedInfo::Color jpegColorType; 201 SkEncodedOrigin exifOrientation; 202 if (!SkGetJpegInfo(data->data(), data->size(), &jpegSize, 203 &jpegColorType, &exifOrientation)) { 204 return false; 205 } 206 bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color; 207 bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color; 208 if (jpegSize != size // Safety check. 209 || !goodColorType 210 || kTopLeft_SkEncodedOrigin != exifOrientation) { 211 return false; 212 } 213 #ifdef SK_PDF_BASE85_BINARY 214 SkDynamicMemoryWStream buffer; 215 SkPDFUtils::Base85Encode(SkMemoryStream::MakeDirect(data->data(), data->size()), &buffer); 216 data = buffer.detachAsData(); 217 #endif 218 219 emit_image_stream(doc, ref, 220 [&data](SkWStream* dst) { dst->write(data->data(), data->size()); }, 221 jpegSize, yuv ? "DeviceRGB" : "DeviceGray", 222 SkPDFIndirectReference(), SkToInt(data->size()), true); 223 return true; 224} 225 226static SkBitmap to_pixels(const SkImage* image) { 227 SkBitmap bm; 228 int w = image->width(), 229 h = image->height(); 230 switch (image->colorType()) { 231 case kAlpha_8_SkColorType: 232 bm.allocPixels(SkImageInfo::MakeA8(w, h)); 233 break; 234 case kGray_8_SkColorType: 235 bm.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType)); 236 break; 237 default: { 238 // TODO: makeColorSpace(sRGB) or actually tag the images 239 SkAlphaType at = bm.isOpaque() ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType; 240 bm.allocPixels(SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, at)); 241 } 242 } 243 // TODO: support GPU images in PDFs 244 if (!image->readPixels(nullptr, bm.pixmap(), 0, 0)) { 245 bm.eraseColor(SkColorSetARGB(0xFF, 0, 0, 0)); 246 } 247 return bm; 248} 249 250void serialize_image(const SkImage* img, 251 int encodingQuality, 252 SkPDFDocument* doc, 253 SkPDFIndirectReference ref) { 254 SkASSERT(img); 255 SkASSERT(doc); 256 SkASSERT(encodingQuality >= 0); 257 SkISize dimensions = img->dimensions(); 258 if (sk_sp<SkData> data = img->refEncodedData()) { 259 if (do_jpeg(std::move(data), doc, dimensions, ref)) { 260 return; 261 } 262 } 263 SkBitmap bm = to_pixels(img); 264 const SkPixmap& pm = bm.pixmap(); 265 bool isOpaque = pm.isOpaque() || pm.computeIsOpaque(); 266 if (encodingQuality <= 100 && isOpaque) { 267 if (sk_sp<SkData> data = img->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality)) { 268 if (do_jpeg(std::move(data), doc, dimensions, ref)) { 269 return; 270 } 271 } 272 } 273 do_deflated_image(pm, doc, isOpaque, ref); 274} 275 276SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img, 277 SkPDFDocument* doc, 278 int encodingQuality) { 279 SkASSERT(img); 280 SkASSERT(doc); 281 SkPDFIndirectReference ref = doc->reserveRef(); 282 if (SkExecutor* executor = doc->executor()) { 283 SkRef(img); 284 doc->incrementJobCount(); 285 executor->add([img, encodingQuality, doc, ref]() { 286 serialize_image(img, encodingQuality, doc, ref); 287 SkSafeUnref(img); 288 doc->signalJobComplete(); 289 }); 290 return ref; 291 } 292 serialize_image(img, encodingQuality, doc, ref); 293 return ref; 294} 295