OLD | NEW |
1 // Copyright 2012 Google Inc. All Rights Reserved. | 1 // Copyright 2012 Google Inc. All Rights Reserved. |
2 // | 2 // |
3 // This code is licensed under the same terms as WebM: | 3 // Use of this source code is governed by a BSD-style license |
4 // Software License Agreement: http://www.webmproject.org/license/software/ | 4 // that can be found in the COPYING file in the root of the source |
5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ | 5 // tree. An additional intellectual property rights grant can be found |
| 6 // in the file PATENTS. All contributing project authors may |
| 7 // be found in the AUTHORS file in the root of the source tree. |
6 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
7 // | 9 // |
8 // main entry for the lossless encoder. | 10 // main entry for the lossless encoder. |
9 // | 11 // |
10 // Author: Vikas Arora (vikaas.arora@gmail.com) | 12 // Author: Vikas Arora (vikaas.arora@gmail.com) |
11 // | 13 // |
12 | 14 |
13 #include <assert.h> | 15 #include <assert.h> |
14 #include <stdio.h> | 16 #include <stdio.h> |
15 #include <stdlib.h> | 17 #include <stdlib.h> |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 // Some other color sits there. | 81 // Some other color sits there. |
80 // Do linear conflict resolution. | 82 // Do linear conflict resolution. |
81 ++key; | 83 ++key; |
82 key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer. | 84 key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer. |
83 } | 85 } |
84 } | 86 } |
85 } | 87 } |
86 argb += pic->argb_stride; | 88 argb += pic->argb_stride; |
87 } | 89 } |
88 | 90 |
89 // TODO(skal): could we reuse in_use[] to speed up ApplyPalette()? | 91 // TODO(skal): could we reuse in_use[] to speed up EncodePalette()? |
90 num_colors = 0; | 92 num_colors = 0; |
91 for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) { | 93 for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) { |
92 if (in_use[i]) { | 94 if (in_use[i]) { |
93 palette[num_colors] = colors[i]; | 95 palette[num_colors] = colors[i]; |
94 ++num_colors; | 96 ++num_colors; |
95 } | 97 } |
96 } | 98 } |
97 | 99 |
98 qsort(palette, num_colors, sizeof(*palette), CompareColors); | 100 qsort(palette, num_colors, sizeof(*palette), CompareColors); |
99 *palette_size = num_colors; | 101 *palette_size = num_colors; |
(...skipping 704 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
804 mem += image_size; | 806 mem += image_size; |
805 enc->argb_scratch_ = mem; | 807 enc->argb_scratch_ = mem; |
806 mem += argb_scratch_size; | 808 mem += argb_scratch_size; |
807 enc->transform_data_ = mem; | 809 enc->transform_data_ = mem; |
808 enc->current_width_ = width; | 810 enc->current_width_ = width; |
809 | 811 |
810 Error: | 812 Error: |
811 return err; | 813 return err; |
812 } | 814 } |
813 | 815 |
814 // Bundles multiple (1, 2, 4 or 8) pixels into a single pixel. | 816 static void ApplyPalette(uint32_t* src, uint32_t* dst, |
815 static void BundleColorMap(const uint8_t* const row, int width, | 817 uint32_t src_stride, uint32_t dst_stride, |
816 int xbits, uint32_t* const dst) { | 818 const uint32_t* palette, int palette_size, |
817 int x; | 819 int width, int height, int xbits, uint8_t* row) { |
818 if (xbits > 0) { | 820 int i, x, y; |
819 const int bit_depth = 1 << (3 - xbits); | 821 int use_LUT = 1; |
820 const int mask = (1 << xbits) - 1; | 822 for (i = 0; i < palette_size; ++i) { |
821 uint32_t code = 0xff000000; | 823 if ((palette[i] & 0xffff00ffu) != 0) { |
822 for (x = 0; x < width; ++x) { | 824 use_LUT = 0; |
823 const int xsub = x & mask; | 825 break; |
824 if (xsub == 0) { | 826 } |
825 code = 0xff000000; | 827 } |
| 828 |
| 829 if (use_LUT) { |
| 830 int inv_palette[MAX_PALETTE_SIZE] = { 0 }; |
| 831 for (i = 0; i < palette_size; ++i) { |
| 832 const int color = (palette[i] >> 8) & 0xff; |
| 833 inv_palette[color] = i; |
| 834 } |
| 835 for (y = 0; y < height; ++y) { |
| 836 for (x = 0; x < width; ++x) { |
| 837 const int color = (src[x] >> 8) & 0xff; |
| 838 row[x] = inv_palette[color]; |
826 } | 839 } |
827 code |= row[x] << (8 + bit_depth * xsub); | 840 VP8LBundleColorMap(row, width, xbits, dst); |
828 dst[x >> xbits] = code; | 841 src += src_stride; |
| 842 dst += dst_stride; |
829 } | 843 } |
830 } else { | 844 } else { |
831 for (x = 0; x < width; ++x) dst[x] = 0xff000000 | (row[x] << 8); | 845 // Use 1 pixel cache for ARGB pixels. |
| 846 uint32_t last_pix = palette[0]; |
| 847 int last_idx = 0; |
| 848 for (y = 0; y < height; ++y) { |
| 849 for (x = 0; x < width; ++x) { |
| 850 const uint32_t pix = src[x]; |
| 851 if (pix != last_pix) { |
| 852 for (i = 0; i < palette_size; ++i) { |
| 853 if (pix == palette[i]) { |
| 854 last_idx = i; |
| 855 last_pix = pix; |
| 856 break; |
| 857 } |
| 858 } |
| 859 } |
| 860 row[x] = last_idx; |
| 861 } |
| 862 VP8LBundleColorMap(row, width, xbits, dst); |
| 863 src += src_stride; |
| 864 dst += dst_stride; |
| 865 } |
832 } | 866 } |
833 } | 867 } |
834 | 868 |
835 // Note: Expects "enc->palette_" to be set properly. | 869 // Note: Expects "enc->palette_" to be set properly. |
836 // Also, "enc->palette_" will be modified after this call and should not be used | 870 // Also, "enc->palette_" will be modified after this call and should not be used |
837 // later. | 871 // later. |
838 static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, | 872 static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, |
839 VP8LEncoder* const enc, int quality) { | 873 VP8LEncoder* const enc, int quality) { |
840 WebPEncodingError err = VP8_ENC_OK; | 874 WebPEncodingError err = VP8_ENC_OK; |
841 int i, x, y; | 875 int i; |
842 const WebPPicture* const pic = enc->pic_; | 876 const WebPPicture* const pic = enc->pic_; |
843 uint32_t* src = pic->argb; | 877 uint32_t* src = pic->argb; |
844 uint32_t* dst; | 878 uint32_t* dst; |
845 const int width = pic->width; | 879 const int width = pic->width; |
846 const int height = pic->height; | 880 const int height = pic->height; |
847 uint32_t* const palette = enc->palette_; | 881 uint32_t* const palette = enc->palette_; |
848 const int palette_size = enc->palette_size_; | 882 const int palette_size = enc->palette_size_; |
849 uint8_t* row = NULL; | 883 uint8_t* row = NULL; |
850 int xbits; | 884 int xbits; |
851 | 885 |
852 // Replace each input pixel by corresponding palette index. | 886 // Replace each input pixel by corresponding palette index. |
853 // This is done line by line. | 887 // This is done line by line. |
854 if (palette_size <= 4) { | 888 if (palette_size <= 4) { |
855 xbits = (palette_size <= 2) ? 3 : 2; | 889 xbits = (palette_size <= 2) ? 3 : 2; |
856 } else { | 890 } else { |
857 xbits = (palette_size <= 16) ? 1 : 0; | 891 xbits = (palette_size <= 16) ? 1 : 0; |
858 } | 892 } |
859 | 893 |
860 err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); | 894 err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); |
861 if (err != VP8_ENC_OK) goto Error; | 895 if (err != VP8_ENC_OK) goto Error; |
862 dst = enc->argb_; | 896 dst = enc->argb_; |
863 | 897 |
864 row = WebPSafeMalloc((uint64_t)width, sizeof(*row)); | 898 row = WebPSafeMalloc((uint64_t)width, sizeof(*row)); |
865 if (row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; | 899 if (row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; |
866 | 900 |
867 for (y = 0; y < height; ++y) { | 901 ApplyPalette(src, dst, pic->argb_stride, enc->current_width_, |
868 for (x = 0; x < width; ++x) { | 902 palette, palette_size, width, height, xbits, row); |
869 const uint32_t pix = src[x]; | |
870 for (i = 0; i < palette_size; ++i) { | |
871 if (pix == palette[i]) { | |
872 row[x] = i; | |
873 break; | |
874 } | |
875 } | |
876 } | |
877 BundleColorMap(row, width, xbits, dst); | |
878 src += pic->argb_stride; | |
879 dst += enc->current_width_; | |
880 } | |
881 | 903 |
882 // Save palette to bitstream. | 904 // Save palette to bitstream. |
883 VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); | 905 VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); |
884 VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM); | 906 VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM); |
885 assert(palette_size >= 1); | 907 assert(palette_size >= 1); |
886 VP8LWriteBits(bw, 8, palette_size - 1); | 908 VP8LWriteBits(bw, 8, palette_size - 1); |
887 for (i = palette_size - 1; i >= 1; --i) { | 909 for (i = palette_size - 1; i >= 1; --i) { |
888 palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); | 910 palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); |
889 } | 911 } |
890 if (!EncodeImageNoHuffman(bw, palette, palette_size, 1, quality)) { | 912 if (!EncodeImageNoHuffman(bw, palette, palette_size, 1, quality)) { |
891 err = VP8_ENC_ERROR_INVALID_CONFIGURATION; | 913 err = VP8_ENC_ERROR_INVALID_CONFIGURATION; |
892 goto Error; | 914 goto Error; |
893 } | 915 } |
894 | 916 |
895 Error: | 917 Error: |
896 free(row); | 918 free(row); |
897 return err; | 919 return err; |
898 } | 920 } |
899 | 921 |
900 // ----------------------------------------------------------------------------- | 922 // ----------------------------------------------------------------------------- |
901 | 923 |
902 static int GetHistoBits(const WebPConfig* const config, | 924 static int GetHistoBits(int method, int use_palette, int width, int height) { |
903 const WebPPicture* const pic) { | |
904 const int width = pic->width; | |
905 const int height = pic->height; | |
906 const uint64_t hist_size = sizeof(VP8LHistogram); | 925 const uint64_t hist_size = sizeof(VP8LHistogram); |
907 // Make tile size a function of encoding method (Range: 0 to 6). | 926 // Make tile size a function of encoding method (Range: 0 to 6). |
908 int histo_bits = 7 - config->method; | 927 int histo_bits = (use_palette ? 9 : 7) - method; |
909 while (1) { | 928 while (1) { |
910 const uint64_t huff_image_size = VP8LSubSampleSize(width, histo_bits) * | 929 const uint64_t huff_image_size = VP8LSubSampleSize(width, histo_bits) * |
911 VP8LSubSampleSize(height, histo_bits) * | 930 VP8LSubSampleSize(height, histo_bits) * |
912 hist_size; | 931 hist_size; |
913 if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; | 932 if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; |
914 ++histo_bits; | 933 ++histo_bits; |
915 } | 934 } |
916 return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS : | 935 return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS : |
917 (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits; | 936 (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits; |
918 } | 937 } |
919 | 938 |
920 static void InitEncParams(VP8LEncoder* const enc) { | 939 static void FinishEncParams(VP8LEncoder* const enc) { |
921 const WebPConfig* const config = enc->config_; | 940 const WebPConfig* const config = enc->config_; |
922 const WebPPicture* const picture = enc->pic_; | 941 const WebPPicture* const pic = enc->pic_; |
923 const int method = config->method; | 942 const int method = config->method; |
924 const float quality = config->quality; | 943 const float quality = config->quality; |
| 944 const int use_palette = enc->use_palette_; |
925 enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4; | 945 enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4; |
926 enc->histo_bits_ = GetHistoBits(config, picture); | 946 enc->histo_bits_ = GetHistoBits(method, use_palette, pic->width, pic->height); |
927 enc->cache_bits_ = (quality <= 25.f) ? 0 : 7; | 947 enc->cache_bits_ = (quality <= 25.f) ? 0 : 7; |
928 } | 948 } |
929 | 949 |
930 // ----------------------------------------------------------------------------- | 950 // ----------------------------------------------------------------------------- |
931 // VP8LEncoder | 951 // VP8LEncoder |
932 | 952 |
933 static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config, | 953 static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config, |
934 const WebPPicture* const picture) { | 954 const WebPPicture* const picture) { |
935 VP8LEncoder* const enc = (VP8LEncoder*)calloc(1, sizeof(*enc)); | 955 VP8LEncoder* const enc = (VP8LEncoder*)calloc(1, sizeof(*enc)); |
936 if (enc == NULL) { | 956 if (enc == NULL) { |
(...skipping 21 matching lines...) Expand all Loading... |
958 const int width = picture->width; | 978 const int width = picture->width; |
959 const int height = picture->height; | 979 const int height = picture->height; |
960 VP8LEncoder* const enc = VP8LEncoderNew(config, picture); | 980 VP8LEncoder* const enc = VP8LEncoderNew(config, picture); |
961 const size_t byte_position = VP8LBitWriterNumBytes(bw); | 981 const size_t byte_position = VP8LBitWriterNumBytes(bw); |
962 | 982 |
963 if (enc == NULL) { | 983 if (enc == NULL) { |
964 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 984 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
965 goto Error; | 985 goto Error; |
966 } | 986 } |
967 | 987 |
968 InitEncParams(enc); | |
969 | |
970 // --------------------------------------------------------------------------- | 988 // --------------------------------------------------------------------------- |
971 // Analyze image (entropy, num_palettes etc) | 989 // Analyze image (entropy, num_palettes etc) |
972 | 990 |
973 if (!VP8LEncAnalyze(enc, config->image_hint)) { | 991 if (!VP8LEncAnalyze(enc, config->image_hint)) { |
974 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 992 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
975 goto Error; | 993 goto Error; |
976 } | 994 } |
977 | 995 |
| 996 FinishEncParams(enc); |
| 997 |
978 if (enc->use_palette_) { | 998 if (enc->use_palette_) { |
979 err = ApplyPalette(bw, enc, quality); | 999 err = EncodePalette(bw, enc, quality); |
980 if (err != VP8_ENC_OK) goto Error; | 1000 if (err != VP8_ENC_OK) goto Error; |
981 // Color cache is disabled for palette. | 1001 // Color cache is disabled for palette. |
982 enc->cache_bits_ = 0; | 1002 enc->cache_bits_ = 0; |
983 } | 1003 } |
984 | 1004 |
985 // In case image is not packed. | 1005 // In case image is not packed. |
986 if (enc->argb_ == NULL) { | 1006 if (enc->argb_ == NULL) { |
987 int y; | 1007 int y; |
988 err = AllocateTransformBuffer(enc, width, height); | 1008 err = AllocateTransformBuffer(enc, width, height); |
989 if (err != VP8_ENC_OK) goto Error; | 1009 if (err != VP8_ENC_OK) goto Error; |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1146 return 0; | 1166 return 0; |
1147 } | 1167 } |
1148 return 1; | 1168 return 1; |
1149 } | 1169 } |
1150 | 1170 |
1151 //------------------------------------------------------------------------------ | 1171 //------------------------------------------------------------------------------ |
1152 | 1172 |
1153 #if defined(__cplusplus) || defined(c_plusplus) | 1173 #if defined(__cplusplus) || defined(c_plusplus) |
1154 } // extern "C" | 1174 } // extern "C" |
1155 #endif | 1175 #endif |
OLD | NEW |