Home | History | Annotate | Download | only in tools
      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