1 /* 2 * Copyright 2018 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 "../third_party/skcms/skcms.h" 9 #include "SkCanvas.h" 10 #include "SkColorSpacePriv.h" 11 #include "SkData.h" 12 #include "SkImage.h" 13 #include "SkStream.h" 14 #include "SkSurface.h" 15 16 static void write_png(const char* path, sk_sp<SkImage> img) { 17 sk_sp<SkData> png = img->encodeToData(); 18 SkFILEWStream(path).write(png->data(), png->size()); 19 } 20 21 int main(int argc, char** argv) { 22 const char* source_path = argc > 1 ? argv[1] : nullptr; 23 if (!source_path) { 24 SkDebugf("Please pass an image or profile to convert" 25 " as the first argument to this program.\n"); 26 return 1; 27 } 28 29 const char* dst_profile_path = argc > 2 ? argv[2] : nullptr; 30 skcms_ICCProfile dst_profile = *skcms_sRGB_profile(); 31 sk_sp<SkData> dst_blob; 32 if (dst_profile_path) { 33 dst_blob = SkData::MakeFromFileName(dst_profile_path); 34 if (!skcms_Parse(dst_blob->data(), dst_blob->size(), &dst_profile)) { 35 SkDebugf("Can't parse %s as an ICC profile.\n", dst_profile_path); 36 return 1; 37 } 38 } 39 40 auto blob = SkData::MakeFromFileName(source_path); 41 42 skcms_ICCProfile src_profile; 43 if (skcms_Parse(blob->data(), blob->size(), &src_profile)) { 44 // Transform white, black, primaries, and primary complements. 45 float src[] = { 46 0,0,0, 47 1,1,1, 48 49 1,0,0, 50 0,1,0, 51 0,0,1, 52 53 0,1,1, 54 1,0,1, 55 1,1,0, 56 }; 57 float dst[24] = {0}; 58 59 if (!skcms_Transform( 60 src, skcms_PixelFormat_RGB_fff, skcms_AlphaFormat_Unpremul, &src_profile, 61 dst, skcms_PixelFormat_RGB_fff, skcms_AlphaFormat_Unpremul, &dst_profile, 62 8)) { 63 SkDebugf("Cannot transform.\n"); 64 return 1; 65 } 66 for (int i = 0; i < 8; i++) { 67 SkDebugf("(%g, %g, %g) --> (%+.4f, %+.4f, %+.4f)\n", 68 src[3*i+0], src[3*i+1], src[3*i+2], 69 dst[3*i+0], dst[3*i+1], dst[3*i+2]); 70 } 71 return 0; 72 } 73 74 sk_sp<SkImage> image = SkImage::MakeFromEncoded(blob); 75 if (!image) { 76 SkDebugf("Couldn't decode %s as an SkImage or an ICC profile.\n", source_path); 77 return 1; 78 } 79 80 image = image->makeRasterImage(); 81 if (!image) { 82 SkDebugf("Converting to raster image failed.\n"); 83 return 1; 84 } 85 86 SkPixmap pixmap; 87 if (!image->peekPixels(&pixmap)) { 88 SkDebugf("We really should be able to peek raster pixels.\n"); 89 return 1; 90 } 91 92 sk_sp<SkColorSpace> dst_cs = SkColorSpace::Make(dst_profile); 93 if (!dst_cs) { 94 SkDebugf("We can't convert to this destination profile as-is. Coercing it.\n"); 95 if (skcms_MakeUsableAsDestinationWithSingleCurve(&dst_profile)) { 96 dst_cs = SkColorSpace::Make(dst_profile); 97 } 98 if (!dst_cs) { 99 SkDebugf("We can't convert to this destination profile at all.\n"); 100 return 1; 101 } 102 } 103 104 { // transform with skcms 105 SkColorSpace* src_cs = image->colorSpace() ? image->colorSpace() 106 : sk_srgb_singleton(); 107 src_cs->toProfile(&src_profile); 108 109 skcms_PixelFormat fmt; 110 switch (pixmap.colorType()) { 111 case kRGBA_8888_SkColorType: fmt = skcms_PixelFormat_RGBA_8888; break; 112 case kBGRA_8888_SkColorType: fmt = skcms_PixelFormat_BGRA_8888; break; 113 default: 114 SkDebugf("color type %d not yet supported, imgcvt.cpp needs an update.\n", 115 pixmap.colorType()); 116 return 1; 117 } 118 119 if (pixmap.alphaType() == kUnpremul_SkAlphaType) { 120 SkDebugf("not premul, that's weird.\n"); 121 return 1; 122 } 123 auto alpha = skcms_AlphaFormat_PremulAsEncoded; 124 125 if (pixmap.rowBytes() != (size_t)pixmap.width() * pixmap.info().bytesPerPixel()) { 126 SkDebugf("not a tight pixmap, need a loop here\n"); 127 return 1; 128 } 129 130 if (!skcms_Transform(pixmap.addr(), fmt,alpha, &src_profile, 131 pixmap.writable_addr(), fmt,alpha, &dst_profile, 132 pixmap.width() * pixmap.height())) { 133 SkDebugf("skcms_Transform() failed\n"); 134 return 1; 135 } 136 pixmap.setColorSpace(dst_cs); 137 138 write_png("transformed-skcms.png", SkImage::MakeRasterCopy(pixmap)); 139 } 140 141 { // transform with writePixels() 142 sk_sp<SkSurface> surface = SkSurface::MakeRaster(pixmap.info().makeColorSpace(dst_cs)); 143 if (!surface) { 144 SkDebugf("couldn't create a surface\n"); 145 return 1; 146 } 147 148 surface->writePixels(pixmap, 0,0); 149 150 write_png("transformed-writepixels.png", surface->makeImageSnapshot()); 151 } 152 153 { // transform by drawing 154 sk_sp<SkSurface> surface = SkSurface::MakeRaster(pixmap.info().makeColorSpace(dst_cs)); 155 if (!surface) { 156 SkDebugf("couldn't create a surface\n"); 157 return 1; 158 } 159 160 surface->getCanvas()->drawImage(image, 0,0); 161 162 write_png("transformed-draw.png", surface->makeImageSnapshot()); 163 } 164 165 return 0; 166 } 167