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